목차
- 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글자만 차지하여 데이터를 저장하게 된다.
동일한 문자열에 대한 char(5)와 char(10)을 비교한다면?
char(5) 타입의 속성 A와 char(10) 타입의 속성 B가 있고, 각 속성에 동일한 문자열 "Avi"가 저장되어 있을 때, 두 속성을 비교한다면, 둘의 실제 데이터 길이가 다르기 때문에 데이터베이스가 자동으로 부족한 빈 공간을 더 채워 동일한 길이를 만든 후 비교한다. (성능 오버헤드)
- 'AA' = 'AA '
- '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를 나타낸다.
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
https://docs.oracle.com/cd/B28359_01/server.111/b28318/datatype.htm#i3253
https://dev.mysql.com/doc/refman/8.0/en/string-types.html
'Programming > Database System' 카테고리의 다른 글
[Database] MySQL ORDER BY 방식 비교와 성능 (0) | 2024.07.16 |
---|---|
[Database] 빅 데이터 (Big Data) 1장 - Big Data Storage System (0) | 2024.04.13 |
[Database] Oracle Database Index (19c 기준) (1) | 2024.01.08 |
[Message Brokers] Streaming data와 Pub / Sub system (1) | 2023.11.25 |
[Database] 인덱스 (Index) 6장 : 쓰기에 최적화된 인덱스 (0) | 2023.11.08 |
댓글