varchar (255) 또는 varchar (256)?


21

테이블을 디자인 할 때 varchar(255)또는 사용해야합니까 varchar(256)? 열 길이에 메타 데이터를 저장하는 데 1 바이트가 사용된다고 들었습니다.

이 시점에서 더 이상 문제가됩니까?

인터넷에서 일부 게시물을 보았지만 Oracle 및 MySQL에 적용됩니다.

Microsoft SQL Server 2016 Enterprise Edition이 있는데이 환경에 어떻게 적용됩니까?

예를 들어, 고객에게 예를 들어 텍스트 설명을 256이 아닌 255 자로 유지하도록 지시하면 어떤 차이가 있습니까? 내가 읽은 내용 "최대 길이는 255 자이므로 DBMS는 단일 바이트를 사용하여 필드의 데이터 길이를 표시 할 수 있습니다. 한계가 256 이상인 경우 2 바이트가 필요합니다." 이것이 사실입니까?


참고 :이 문제는 MSDN 포럼에 교차 게시 : social.msdn.microsoft.com/Forums/sqlserver/en-US/...
솔로몬 Rutzky

답변:


36

열의 크기를 적절하게 조정하십시오. 각 열에 "표준"크기를 사용하지 마십시오. 30 자만 필요한 경우 255를 처리 할 수있는 열을 작성하는 이유는 무엇입니까? varchar(max)문자열 열 사용 을 옹호하지 않아서 기쁩니다 .

열을 인덱싱해야하거나 열을 기본 키로 사용하고 외래 키 참조가있는 경우 특히주의해야합니다. SQL Server는 쿼리 최적화 프로그램의 각 열 크기를 사용하여 쿼리 처리를위한 예상 메모리 요구 사항을 이해합니다. 열이 너무 크면 성능이 저하 될 수 있습니다.

너무 큰 열의 인덱스는 오류가 생성 될 수 있습니다.

CREATE TABLE dbo.WideIndex
(
    col1 varchar(255) NOT NULL
    , col2 varchar(255) NOT NULL
    , col3 varchar(600) NOT NULL    
);

CREATE INDEX IX_WideIndex_01
ON dbo.WideIndex (col1, col2, col3);

위의 색인을 작성하면 다음과 같은 경고가 발생합니다.

경고! 최대 키 길이는 900 바이트입니다. 인덱스 'IX_WideIndex_01'의 최대 길이는 1110 바이트입니다. 큰 값의 일부 조합의 경우 삽입 / 업데이트 작업이 실패합니다.

900 바이트는 클러스터 된 인덱스 (및 SQL Server 2012 및 그 이전 버전의 클러스터되지 않은 인덱스)의 최대 키 크기입니다. 1700 바이트는 최신 버전의 SQL Server에서 비 클러스터형 인덱스의 최대 키 크기입니다. (255)와 같이 일반적인 너비의 열을 디자인하면이 경고가 예상보다 훨씬 자주 발생할 수 있습니다.

저장소 내부에 관심이있는 경우 다음과 같은 작은 테스트를 사용하여 SQL Server가 압축되지 않은 행 저장소 데이터를 저장하는 방법을 더 잘 이해할 수 있습니다.

먼저 다양한 크기의 열을 저장할 수있는 테이블을 만듭니다.

IF OBJECT_ID(N'dbo.varchartest', N'U') IS NOT NULL
DROP TABLE dbo.varchartest;
GO

CREATE TABLE dbo.varchartest
(
    varchar30 varchar(30) NOT NULL
    , varchar255 varchar(255) NOT NULL
    , varchar256 varchar(256) NOT NULL
);

이제 하나의 행을 삽입합니다 :

INSERT INTO dbo.varchartest (varchar30, varchar255, varchar256)
VALUES (REPLICATE('1', 30), REPLICATE('2', 255), REPLICATE('3', 256));

이 쿼리는 문서화되지 않은 지원되지 않는 함수를 사용 sys.fn_RowDumpCracker하고 sys.fn_PhyslocCracker테이블에 대한 몇 가지 흥미로운 세부 사항을 표시합니다.

SELECT rdc.*
    , plc.*
FROM dbo.varchartest vct
CROSS APPLY  sys.fn_RowDumpCracker(%%rowdump%%) rdc
CROSS APPLY sys.fn_physlocCracker(%%physloc%%) plc

출력은 다음과 유사합니다.

╔ ====================== ╦ ============= ╦ ============== ======= ╦ ============================================== ========= ╦ ======================== ╦ ================== == ╗
_ partition_id ║ colName ║ IsInrow ║ IsSparse ║ IsRecordPrefixCompressed ║ IsSymbol ║ PrefixBytes ║ InRowLength ║ file_id ║ page_id ║ slot_id ║
╠ ====================== ╬ ============= ╬ =============== ======= ╬ =============================================== ========= ╬ ======================== ╬ ================== == ╣
║ 1729382263096344576 ║ varchar30 ║ 1 ║ 0 ║ 0 ║ 0 ║ 0 ║ 30 ║ 1 ║ 1912 ║ 0 ║
║ 1729382263096344576 ║ varchar255 ║ 1 ║ 0 ║ 0 ║ 0 ║ 0 ║ 255 ║ 1 ║ 1912 ║ 0 ║
║ 1729382263096344576 ║ varchar256 ║ 1 ║ 0 ║ 0 ║ 0 ║ 0 ║ 256 ║ 1 ║ 1912 ║ 0 ║
╚ ====================== ╩ ============= ╩ =============== ======= ╩ =============================================== ========= ╩ ======================== ╩ ================== == ╝

보다시피 InRowLength, "file_id", "page_id"및 "slot_id"와 같이 각 행의 실제 저장 위치와 함께 각 값의가 표시됩니다.

위의 쿼리 결과에서 file_idpage_id값을 가져 와서 실행 DBCC PAGE하면 실제 실제 페이지 내용을 볼 수 있습니다.

DBCC TRACEON (3604); --send display to the client
DBCC PAGE (tempdb, 1, 1912, 3); --database, file_id, page_id, 3 to show page contents
DBCC TRACEOFF (3604);--reset display back to the error log

내 컴퓨터의 결과는 다음과 같습니다.

페이지 : (1 : 1912)


완충기:


BUF @ 0x00000000FF5B2E80

bpage = 0x0000000024130000 bhash = 0x0000000000000000 bpageno = (1 : 1912)
bdbid = 2 참조 = 0 bcputicks = 0
bsampleCount = 0 bUse1 = 32497 bstat = 0x10b
블로그 = 0x212121cc bnext = 0x0000000000000000          

페이지 헤더 :


페이지 @ 0x0000000024130000

m_pageId = (1 : 1912) m_headerVersion = 1 m_type = 1
m_typeFlagBits = 0x0 m_level = 0 m_flagBits = 0x8000
m_objId (AllocUnitId.idObj) = 98834 m_indexId (AllocUnitId.idInd) = 7936
메타 데이터 : AllocUnitId = 2233785421652951040                              
메타 데이터 : PartitionId = 1945555045333008384 메타 데이터 : IndexId = 0
메타 데이터 : ObjectId = 34099162 m_prevPage = (0 : 0) m_nextPage = (0 : 0)
pminlen = 4 m_slotCnt = 1 m_freeCnt = 7538
m_freeData = 652 m_reservedCnt = 0 m_lsn = (35 : 210971 : 362)
m_xactReserved = 0 m_xdesId = (0 : 0) m_ghostRecCnt = 0
m_tornBits = 0 DB Frag ID = 1                      

할당 상태

GAM (1 : 2) = 할당 된 SGAM (1 : 3) = 할당되지 않은 PFS (1 : 1) = 0x41 할당 된 50_PCT_FULL
DIFF (1 : 6) = 변경되지 않은 ML (1 : 7) = NOT MIN_LOGGED           

슬롯 0 오프셋 0x60 길이 556

레코드 유형 = PRIMARY_RECORD 레코드 속성 = NULL_BITMAP VARIABLE_COLUMNS
레코드 크기 = 556                   
메모리 덤프 @ 0x000000005145A060

0000000000000000 : 30000400 03000003 002d002c 012c0231 31313131 0 ........-.,.,. 11111
0000000000000014 : 31313131 31313131 31313131 31313131 31313131 11111111111111111111
0000000000000028 : 31313131 31323232 32323232 32323232 32323232 11111222222222222222
000000000000003C : 32323232 32323232 32323232 32323232 32323232 22222222222222222222
0000000000000050 : 32323232 32323232 32323232 32323232 32323232 22222222222222222222
0000000000000064 : 32323232 32323232 32323232 32323232 32323232 22222222222222222222
0000000000000078 : 32323232 32323232 32323232 32323232 32323232 22222222222222222222
000000000000008C : 32323232 32323232 32323232 32323232 32323232 22222222222222222222
00000000000000A0 : 32323232 32323232 32323232 32323232 32323232 22222222222222222222
B4 : 32323232 32323232 32323232 32323232 32323232 22222222222222222222
C : 32323232 32323232 32323232 32323232 32323232 22222222222222222222
DC : 32323232 32323232 32323232 32323232 32323232 22222222222222222222
00 : 000000000000F0 : 32323232 32323232 32323232 32323232 32323232 22222222222222222222
0000000000000104 : 32323232 32323232 32323232 32323232 32323232 22222222222222222222
0000000000000118 : 32323232 32323232 32323232 32323232 32323232 22222222222222222222
000000000000012C : 33333333 33333333 33333333 33333333 33333333 33333333333333333333
0000000000000140 : 33333333 33333333 33333333 33333333 33333333 33333333333333333333
0000000000000154 : 33333333 33333333 33333333 33333333 33333333 33333333333333333333
0000000000000168 : 33333333 33333333 33333333 33333333 33333333 33333333333333333333
000000000000017C : 33333333 33333333 33333333 33333333 33333333 33333333333333333333
0000000000000190 : 33333333 33333333 33333333 33333333 33333333 33333333333333333333
00000000000001A4 : 33333333 33333333 33333333 33333333 33333333 33333333333333333333
00000000000001B8 : 33333333 33333333 33333333 33333333 33333333 33333333333333333333
CC : 33333333 33333333 33333333 33333333 33333333 33333333333333333333
00000000000001E0 : 33333333 33333333 33333333 33333333 33333333 33333333333333333333
00000000000001F4 : 33333333 33333333 33333333 33333333 33333333 33333333333333333333
0000000000000208 : 33333333 33333333 33333333 33333333 33333333 33333333333333333333
C : 33333333 33333333 33333333 33333333 3333333333333333

슬롯 0 열 1 오프셋 0xf 길이 30 길이 (물리적) 30

varchar30 = 111111111111111111111111111111                               

슬롯 0 열 2 오프셋 0x2d 길이 255 길이 (물리) 255

varchar255 = 2222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222
22222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222
222222222222222222222222222222222222222222                               

슬롯 0 열 3 오프셋 0x12c 길이 256 길이 (물리적) 256

varchar256 = 3333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333
33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333
3333333333333333333333333333333333333333333333                              

16

다른 사람들은 길이를 저장하는 데 필요한 바이트 수가 고정되어 있다고 이미 지적했습니다. 나는 당신의 질문 에서이 부분에 집중하고 싶었습니다.

이 시점에서 더 이상 문제가됩니까?

질문에 Enterprise Edition 태그가 지정되어 있으며 이는 일반적으로 상당한 양의 데이터가 있음을 의미합니다. 행당 1 바이트의 차이는 실제로 실제로 그렇게 중요하지 않습니다. 예를 들어, 완전히 채워진 VARCHAR(255)열이 있는 다음 테이블 은 디스크에서 143176KB의 공간을 차지합니다.

DROP TABLE IF EXISTS dbo.V255_FULL;

CREATE TABLE dbo.V255_FULL (
    ID1 BIGINT NOT NULL,
    ID2 BIGINT NOT NULL,
    V255 VARCHAR(255)
);

INSERT INTO dbo.V255_FULL WITH (TABLOCK)
SELECT TOP (500000) 0, 0, REPLICATE('A', 255)
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;

EXEC sp_spaceused 'V255_FULL';

결과 :

╔═══════════╦══════════════════════╦═══════════╦═══════════╦════════════╦════════╗
   name             rows          reserved     data     index_size  unused 
╠═══════════╬══════════════════════╬═══════════╬═══════════╬════════════╬════════╣
 V255_FULL  500000                143176 KB  142888 KB  8 KB        280 KB 
╚═══════════╩══════════════════════╩═══════════╩═══════════╩════════════╩════════╝

완전히 채워진 VARCHAR(256)열이 있는 두 번째 테이블을 만들어 봅시다 . 행 당 적어도 하나 이상의 바이트가 필요합니다.

DROP TABLE IF EXISTS dbo.V256_FULL;

CREATE TABLE dbo.V256_FULL (
    ID1 BIGINT NOT NULL,
    ID2 BIGINT NOT NULL,
    V256 VARCHAR(256)
);

INSERT INTO dbo.V256_FULL WITH (TABLOCK)
SELECT TOP (500000) 0, 0, REPLICATE('A', 256)
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;

EXEC sp_spaceused 'V256_FULL';

결과 :

╔═══════════╦══════════════════════╦═══════════╦═══════════╦════════════╦════════╗
   name             rows          reserved     data     index_size  unused 
╠═══════════╬══════════════════════╬═══════════╬═══════════╬════════════╬════════╣
 V256_FULL  500000                143176 KB  142888 KB  8 KB        280 KB 
╚═══════════╩══════════════════════╩═══════════╩═══════════╩════════════╩════════╝

두 테이블 모두 동일한 양의 공간을 차지합니다. 동일한 수의 행이 각 8k 페이지에 맞습니다. 응용 프로그램을 최적화하는 데 시간을 투자하고 싶지만 다른 영역에 집중하는 것이 좋습니다.


7

선언 된 varchar 크기는 성능에 영향을 미치지 않습니다. 데이터는 실제로 페이지 압축 또는 행 압축을 사용하여 행 저장소로 저장 될 수 있습니다. 클러스터 된 열 저장소 또는 메모리 최적화 테이블. 이들 각각은 서로 다른 성능 상충 관계를 갖지만 varchar (255) 또는 varchar (256)을 선언하는지 여부는 중요하지 않습니다.


9
@ DavidBrowne-Microsoft 아니요, "varchar의 선언 된 크기는 성능에 영향을 미치지 않습니다"는 사실이 아닙니다. 데이터 형식 크기는 쿼리의 메모리 부여에 영향을줍니다. 자세한 내용은 brentozar.com/archive/2017/02/memory-grants-data-size 를 참조하십시오.
브렌트 오자르

6
단순하게 유지하고 조기 최적화를 권장하지 않습니다.
David Browne-Microsoft
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.