본문 바로가기
Programming/Database System

[Database] 데이터 타입 비교 : char vs varchar

by kghworks 2024. 1. 17.

목차

  • SQL 표준에서의 char, varchar
  • 예시 1. Oracle
  • 예시 2. MySQL

 


SQL 표준에서의 char, varchar

SQL 표준에서 정의한 built-in type은 다음과 같다.

  • char(n) : character, 고정길이 형태 문자열, n (bytes)은 사용자가 정의
  • varchar(n) : character varying, 가변길이 형태 문자열, 사용자는 최대길이 n (bytes)을 정의
  • nvarchar (n) : 다국어 데이터를 저장할 수 있는 varchar
  • int
  • samllint
  • numeric(p, d)
  • real, double precision
  • folat(n)

 char와 varchar에 대해 간단히 비교해보려 한다. 둘 다 문자열 데이터 타입이고, null을 삽입할 수 있다. 

 

고정길이 char vs 가변길이 varchar

 char 데이터 타입은 고정길이 문자열을 저장한다. 예를 들어 char(10)으로 데이터 타입을 선언한 뒤 "Avi"를 저장하면, 7개의 빈 공간을 만들어 고정된 크기 10으로 저장한다.  반면, varchar(10)으로 속성의 데이터 타입을 선언했다고 한다면, "Avi"를 저장했을 때 빈 공간 없이 3글자만 차지하여 데이터를 저장하게 된다. 

출처 :https://dev.mysql.com/doc/refman/8.0/en/char.html

 

동일한 문자열에 대한 char(5)와 char(10)을 비교한다면?

 char(5) 타입의 속성 A와 char(10) 타입의 속성 B가 있고, 각 속성에 동일한 문자열 "Avi"가 저장되어 있을 때, 두 속성을 비교한다면, 둘의 실제 데이터 길이가 다르기 때문에 데이터베이스가 자동으로 부족한 빈 공간을 더 채워 동일한 길이를 만든 후 비교한다. (성능 오버헤드) 

  1. 'AA' = 'AA   '
  2. 'AA   ' = 'AA   '

 

char와 varchar 간의 비교

 데이터베이스 시스템에 따라 다르다. 동일한 문자열 "Avi"를 각각 저장하고 있더라도 A=B는 false를 리턴할 가능성도 있다. 이런 문제가 발생하는 데이터베이스인 경우 varchar 타입을 추천한다. 

 

char가 적합한 경우

 고정길이 문자열을 저장해야 하는 데이터 타입에 적절하다. 주민번호, 사원번호, 휴대폰번호, 자동차 번호판 등이 해당한다.

 그런데 휴대폰 번호가 꼭 n자릿수일 수 있을까? 생각해 보면 과거에 017-xxx-xxxx처럼 가운데가 세 자리였지만 현재는 010-xxxx-xxxx와 같이 가운데는 4자리다. 그럼 과거에 char 타입으로 선언하면서 미래를 대비해 한자리 크게 선언해야 할까? 그럼 대부분의 데이터가 1 byte를 빈 공간을 차지하게 될 것인데? 고민해 볼 문제다.

 'Y', 'N'과 같이 boolean에 해당하는 데이터 타입을 표현하고 싶을 때 적합하다고 생각한다. (개인적인 생각)

 

varchar가 적합한 경우

이름, 주소 등과 같이 가변길이 문자열이 필요한 경우 적절하다. char의 경우 가변길이에 대응하기 위해서는 큰 크기로 선언하여 데이터마다 빈 공간을 차지하는 공간 오버헤드를 발생시키지만, varchar의 경우 실제 데이터에 해당하는 크기만큼만 데이터를 저장하기 때문에 공간 상 이점을 가질 수 있다.


예시 1. Oracle (11g 기준)

 https://docs.oracle.com/ 을 참고했다. 

 

 CHAR 타입의 경우 1 ~ 2,000 bytes를 지원한다. (기본값 : 1 byte)

 고정길이 문자열 타입으로 VARCHAR2와 VARCHAR가 있는데 둘은 동의어로 Oracle에서는 변경을 최소화 VARCHAR2로 통일하기를 권고한다. VARCHAR2의 경우 1 ~ 4,000 bytes를 지원한다. 

The VARCHAR datatype is synonymous with the VARCHAR2 datatype. To avoid possible changes in behavior, always use the VARCHAR2 datatype to store variable-length character strings.

 

* 출처 : https://docs.oracle.com/cd/B28359_01/server.111/b28318/datatype.htm#i1835

CREATE TABLE TMP_TB1
(
    SEQ          NUMBER(10)     NOT NULL,
    COL_VARIABLE VARCHAR2(4001) NOT NULL,
    COL_FIXED    CHAR(2001)       NOT NULL
);

 

 최댓값이 넘는 크기를 할당할 경우 ORA-00910: 데이터형에 지정된 길이가 너무 깁니다 에러를 반환한다.

 

INSERT INTO TMP_TB1 (SEQ, COL_VARIABLE, COL_FIXED)
VALUES (1, 'Avi', 'Avi');

COMMIT;

SELECT COL_FIXED,
       LENGTHB(COL_FIXED) AS "CHAR 길이",
       LENGTH(COL_FIXED)     AS "CHAR bytes 수",
       COL_VARIABLE,
       LENGTHB(COL_VARIABLE)    AS "VARCHAR2 길이",
       LENGTH(COL_VARIABLE)  AS "VARCHAR2 bytes 수"
FROM TMP_TB1;

 CHAR 형 데이터에 대한 LEGNTH는 우측 공백을 제외한 데이터 길이를 반환하지만 LENTHB()를 통해 바이트 수를 조회해 보면 10 bytes (고정크기)를 차지하고 있는 것을 알 수 있다.

-- result : none
SELECT *
FROM TMP_TB1
WHERE COL_FIXED = COL_VARIABLE;

-- result : 1   Avi     Avi
SELECT *
FROM TMP_TB1
WHERE RTRIM(COL_FIXED) = COL_VARIABLE;

 

 CHAR와 VARCHAR2 간의 비교 시 CHAR의 오른쪽 공백으로 인해 일치 여부에 fale를 리턴하며, RTRIM()으로 우측 공백을 제거한 뒤 정상적으로 비교가 됨을 알 수 있다.

 

아래처럼 CHAR에 공백 (' ')을 포함해서 저장하면 어떨까.

INSERT INTO TMP_TB1 (SEQ, COL_VARIABLE, COL_FIXED)
VALUES (2, 'Carol ', 'Carol ');

commit;

SELECT *
FROM TMP_TB1
WHERE RTRIM(COL_FIXED) = COL_VARIABLE;

 

 원했던 `Carol `은 등장하지 않는다. 왜냐면, RTRIM()으로 CHAR 자릿수를 위해 추가된 공백 4 bytes와 데이터 insert 시에 저장한 진짜 공백 1 bytes를 모두 지워버리기 때문이다. 

SELECT *
FROM TMP_TB1
WHERE RTRIM(COL_FIXED) = RTRIM(COL_VARIABLE);

 

위처럼 둘 다 모두 오른쪽 공백을 제거하여 비교할 수 있다. 그러나 사용자가 의도하고 오른쪽에 넣은 공백을 제거하는 것이기 때문에 표현 자체가 매끄럽지는 않다. 그렇기 때문에 동일한 Data에 대해서 서로 다른 데이터 타입을 혼용해서 사용하면 안 되는 것이다. 


예시 1. MySQL (8.0 기준)

https://dev.mysql.com/doc/ 을 참고했다.

 

 CHAR는 0 ~ 255 bytes를 지원한다. 더 작은 길이형의 데이터의 경우 오른쪽에 공백을 추가하여 저장하는데, PAD_CHAR_TO_FULL_LENGTH sql 모드를 지정하면, 공백을 포함하여 데이터를 리턴 받을 수 있다. (8.0.13부터 deprecated)

 VARCHAR는  0 ~ 65,535 bytes를 지원한다. CHAR와 다르게 VARCHAR에는 1~2byte를 추가로 저장한다. 해당 byte에는 컬럼에 저장된 실제 값의  bytes를 나타낸다. 

 

같은 value에 대한 CHAR(4), VARCHAR(4)의 차이

CREATE TABLE vc
(
    col_variable VARCHAR(10),
    col_fixed    CHAR(10)
);

INSERT INTO vc
VALUES ('Carol     ', 'Carol     ');

SELECT CONCAT('(', col_variable, ')'), CONCAT('(', col_fixed, ')')
FROM vc;

 

 

 아래 케이스를 보자. CHAR 타입의 컬럼에 UNIQUE 인덱스를 생성했다면, 후행 pad character (CHAR 타입의 크기를 맞추기 위해 추가되는 빈 문자열)을 주의해서 값을 넣도록 하자. 안 그러면 인덱스 제약조건 오류가 발생한다. (Duplicate entry 'Carol' for key 'ux_col_fixed')

DROP TABLE  vc;
CREATE TABLE vc
(
    col_fixed    CHAR(10),
    UNIQUE INDEX ux_col_fixed (col_fixed)
);

INSERT INTO vc VALUES ('Carol');
INSERT INTO vc VALUES ('Carol    '); # Duplicate entry 'Carol' for key 'ux_col_fixed'

 


참고

https://product.kyobobook.co.kr/detail/S000001693775

 

데이터베이스 시스템 | Abraham Silberschatz - 교보문고

데이터베이스 시스템 | 데이터베이스에 대한 거의 모든 것 이 책은 전통적인 관계형 데이터베이스의 이론부터 블록체인 데이터베이스와 같은 최신 기술까지 다룹니다. 관계형 데이터베이스 모

product.kyobobook.co.kr

https://docs.oracle.com/cd/B28359_01/server.111/b28318/datatype.htm#i3253

 

Oracle Data Types

33/35 26 Oracle Data Types This chapter discusses the Oracle built-in datatypes, their properties, and how they map to non-Oracle datatypes. This chapter includes the following topics: Overview of Character Datatypes The character datatypes store character

docs.oracle.com

https://dev.mysql.com/doc/refman/8.0/en/string-types.html

 

MySQL :: MySQL 8.0 Reference Manual :: 11.3 String Data Types

MySQL 8.0 Reference Manual  /  Data Types  /  String Data Types The string data types are CHAR, VARCHAR, BINARY, VARBINARY, BLOB, TEXT, ENUM, and SET. For information about storage requirements of the string data types, see Section 11.7, “Data Type

dev.mysql.com

 

댓글