2014년 6월 18일 수요일

VARCHAR(8000) 사용시 주의 사항

왜 하필 VARCHAR(8000)이냐구요??
왜냐하면 VARCHAR(8000)은 특별하기 때문이죠. ^^

일단 기본적인 사항부터 점검하겠습니다.
VARCHAR(8) 컬럼을 가진 테이블 하나를 만들어보겠습니다.
CREATE TABLE TBL1 (COL1 VARCHAR(8))
이 테이블에 데이터를 넣어보겠습니다.
INSERT INTO TBL1 VALUES ('초짜해커')
아무 문제 없죠? 그럼 이 테이블에 데이터가 8Byte를 넘는 데이터를 넣어보겠습니다.
INSERT INTO TBL1 VALUES (REPLICATE('A', 1) + '초짜해커')
어떻게 될까요? 에러가 납니다.
메시지 8152, 수준 16, 상태 14, 줄 1
문자열이나 이진 데이터는 잘립니다.
문이 종료되었습니다.
일반적으로 허용 범위 이상의 데이터를 넣으려고 하면 에러가 발생합니다. 이번에는 VARCHAR(8000)을 가진 테이블을 하나 만들어보겠습니다.
CREATE TABLE TBL2 (COL1 VARCHAR(8000))
자... 이번에는 바로 8000Byte를 넘는 데이터를 넣어보겠습니다. 당연히 에러가 나겠죠?
INSERT INTO TBL2 VALUES (REPLICATE('A', 7993) + '초짜해커')
어찌된 일인지 에러 없이 행이 추가됩니다. 도데체 어떤 데이터가 들어 있는지 조회해보겠습니다.
SELECT COL1 = RIGHT(COL1, 10) FROM TBL2
결과는
COL1
--------------------
AAAAAA초짜해?
마지막 문자열이 좀 이상하게 들어갔네요. 실제로 어떤 값이 들어 있는지 Binary로 변환해서 살펴보겠습니다.
SELECT 원문 = RIGHT(COL1, 5), 이진값 = CONVERT(VARBINARY(100), RIGHT(COL1, 5)) FROM TBL2
결과는
원문        이진값
--------    ------------------------
A초짜해?    0x41C3CAC2A5C7D8C4
입니다. "초짜해커"의 Binary값은 0xC3CAC2A5C7D8C4BF 인데 맨 마지막 C4BF중에 BF는 잘리고 C4까지만 들어갔습니다. 테이블에 아무 문제 없는것인지 DBCC CHECKTABLE(TBL2)를 해보면 에러가 납니다.
'TBL2'의 DBCC 결과입니다.
메시지 2570, 수준 16, 상태 3, 줄 1
개체 ID 2121058592, 인덱스 ID 0, 파티션 ID 72057594038845440, 할당 단위 ID 72057594039894016("In-row data" 유형)의 페이지 (1:80), 슬롯 0. 열 "COL1" 값이 데이터 형식 "varchar"의 범위를 벗어났습니다.
열을 올바른 값으로 업데이트하십시오.
1개 페이지에 개체 "TBL2"에 대한 행이 0개 있습니다.
CHECKTABLE이(가) 테이블 'TBL2'(개체 ID 2121058592)에서 0개의 할당 오류와 1개의 일관성 오류를 찾았습니다.
DBCC 실행이 완료되었습니다. DBCC에서 오류 메시지를 출력하면 시스템 관리자에게 문의하십시오.
COL1의 값이 데이터 형식 varchar의 범위를 벗어났다는군요. 문제가 무엇일까요? 저는 심심하던 차에 VARCHAR(2)부터 VARCHAR(8000)까지 테이블을 만들어서 딱 1Byte를 넘는 데이터를 모두 넣어봤습니다. 8000이하에서는 모두 에러를 내면서 행이 추가되지 않았는데 8000일때만 데이터가 들어갔습니다. 노가다 안하고 스크립트로 순식간에 했습니다. 왜 이런일이 일어날까요? 이 문제는 "+" 연산자의 문제인데요. 자세한 내용은 MSDN에 있습니다. 문서를 잘 읽어보면 주의사항에
문자열 연결의 결과가 제한치인 8,000바이트를 초과하면 결과가 잘립니다.
하지만 연결된 문자열 중 하나 이상이 큰 값 형식이면 잘림은 발생하지 않습니다.
라고 나옵니다. 여기서 "큰 값 형식"이란 VARCHAR(MAX)등을 의미하구요. VARCHAR(MAX)와 연산하지 않는 이상은 8000Byte 이상은 잘린다는 설명입니다. 앞의 예에서 살펴보면 "A" 7993개와 "초짜해커"를 "+"연산을 하면 마지막 "커"가 잘린다는 거죠. 그런데 "커"는 2Byte(0xC4BF)이기 때문에 뒤쪽 1Byte(0xBF)만 잘리고 앞쪽 1Byte(0xC4가 문자열에 포함되는 것이죠. 문제는 이 반쪽짜리 1Byte가 VARCHAR형식에서는 포함될 수 없는 데이터이기 때문에 일관성 오류가 일어나는 것입니다. 요약하면 VARCHAR(8000)에 "A......A초짜해커"를 INSERT하려고 하면 에러가 없이 INSERT되는것이 아니라 "커"의 앞 1Byte까지만 잘려서 들어가는 것이고, 이게 문제를 일으킨다는 것입니다. 문자열을 연산할때는 항상 이 8000을 주의해야 하는데요. 만약 VARCHAR(8000)컬럼 두개를 연결해서 계산된 컬럼을 만들면 어떻게 될까요? 두번째 컬럼의 일부분은 잘리게 됩니다. 그래서 적절히 VARCHAR(MAX)로 형변환 후 연산해야 합니다.

댓글 1개: