varchar (255) หรือ varchar (256)?


21

ฉันควรใช้varchar(255)หรือvarchar(256)เมื่อออกแบบตาราง? ฉันได้ยินมาว่ามีการใช้หนึ่งไบต์สำหรับความยาวของคอลัมน์หรือเพื่อเก็บข้อมูลเมตา

มันสำคัญอีกต่อไปแล้ว ณ จุดนี้?

ฉันเห็นโพสต์บนอินเทอร์เน็ตอย่างไรก็ตามพวกเขาใช้กับ Oracle และ MySQL

เรามี Microsoft SQL Server 2016 Enterprise Edition แล้วนำไปใช้กับสภาพแวดล้อมนี้ได้อย่างไร

ตอนนี้พูดตัวอย่างเช่นถ้าฉันบอกให้ลูกค้าของฉันเก็บตัวอย่างคำอธิบายข้อความถึง 255 ตัวอักษรแทน 256 จะมีความแตกต่าง? สิ่งที่ฉันอ่าน "ด้วยความยาวสูงสุด 255 อักขระ DBMS สามารถเลือกใช้ไบต์เดียวเพื่อระบุความยาวของข้อมูลในฟิลด์ถ้าขีด จำกัด เป็น 256 หรือมากกว่านั้นจำเป็นต้องใช้สองไบต์" มันเป็นเรื่องจริงเหรอ?


FYI: คำถามนี้ถูกโพสต์ข้ามบนฟอรัม 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 ║ IsRecordPrefix การบีบอัด║ IsSymbol ║คำนำหน้าไบต์
╠═════════════════════╬════════════╬═════════╬════ ══════╬══════════════════════════╬══════════╬═════ ════════╬═════════════╬═════════╬═════════╬═══════ ══╣
║ 1729382263096344576 ║ varchar30 ║ 1 ║ 0 ║ 0 ║ 0 ║ 0 ║ 30 ║ 1 ║ 1912 ║ 0 ║
║ 1729382263096344576 ║ varchar255 ║ 1 ║ 0 ║ 0 ║ 0 ║ 0 ║ 0 ║ 255 ║ 1 ║ 1912 ║ 0 ║
║ 1729382263096344576 ║ varchar256 ║ 1 ║ 0 ║ 0 ║ 0 ║ 0 ║ 0 ║ 256 ║ 1 ║ 1912 ║ 0 ║
╚═════════════════════╩════════════╩═════════╩════ ══════╩══════════════════════════╩══════════╩═════ ════════╩═════════════╩═════════╩═════════╩═══════ ══╝

อย่างที่คุณเห็นค่าInRowLengthสำหรับแต่ละค่าจะปรากฏพร้อมกับตำแหน่งที่เก็บข้อมูลฟิสิคัลของแต่ละแถว - "file_id", "page_id" และ "slot_id"

หากเรานำค่าfile_idและpage_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 = 0x00000000000000000000 bpageno = (1: 1912)
bdbid = 2 Breferences = 0 bcputicks = 0
bsampleCount = 0 bUse1 = 32497 bstat = 0x10b
บล็อก = 0x212121cc bnext = 0x00000000000000000000          

ส่วนหัวของหน้า:


หน้า @ 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) = ไม่ MIN_LOGGED           

ช่อง 0 ออฟเซ็ต 0x60 ความยาว 556

ประเภทระเบียน = Primary_RECORD คุณสมบัติบันทึก = NULL_BITMAP VARIABLE_COLUMNS
ขนาดบันทึก = 556                   
Memory Dump @ 0x000000005145A060

00000000000000000000: 30000400 03000003 002d002c 012c0231 31313131 0 ........-.,.,. 11111
0000000000000014: 31313131 31313131 31313131 31313131 31313131 111111111111111111111111
0000000000000028: 31313131 31323232 32323232 32323232 32323232 1111122222222222222222
000000000000003C: 32323232 32323232 32323232 32323232 32323232 222222222222222222222222
0000000000000050: 32323232 32323232 32323232 32323232 32323232 222222222222222222222222
0000000000000064: 32323232 32323232 32323232 32323232 32323232 222222222222222222222222
0000000000000078: 32323232 32323232 32323232 32323232 32323232 222222222222222222222222
000000000000008C: 32323232 32323232 32323232 32323232 32323232 222222222222222222222222
000000000000000000A0: 32323232 32323232 32323232 32323232 32323232 222222222222222222222222
00000000000000BB4: 32323232 32323232 32323232 32323232 32323232 222222222222222222222222
00000000000000CC8: 32323232 32323232 32323232 32323232 32323232 222222222222222222222222
00000000000000DCDC: 32323232 32323232 32323232 32323232 32323232 222222222222222222222222
00000000000000FF0: 32323232 32323232 32323232 32323232 32323232 222222222222222222222222
0000000000000104: 32323232 32323232 32323232 32323232 32323232 222222222222222222222222
0000000000000118: 32323232 32323232 32323232 32323232 32323232 222222222222222222222222
000000000000012C: 33333333 33333333 33333333 33333333 33333333 33333333333333333333
0000000000000140: 33333333 33333333 33333333 33333333 33333333 333333333333333333
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 333333333333333333
00000000000001A4: 33333333 33333333 33333333 33333333 33333333 333333333333333333
00000000000001B8: 33333333 33333333 33333333 33333333 33333333 333333333333333333
00000000000001CC: 33333333 33333333 33333333 33333333 33333333 333333333333333333
00000000000001E0: 33333333 33333333 33333333 33333333 33333333 333333333333333333
00000000000001F4: 33333333 33333333 33333333 33333333 33333333 333333333333333333
0000000000000208: 33333333 33333333 33333333 33333333 33333333 33333333333333333333
000000000000021C: 33333333 33333333 33333333 33333333 33333333333333

ช่อง 0 คอลัมน์ 1 ชดเชย 0xf ความยาว 30 ความยาว (ทางกายภาพ) 30

varchar30 = 11111111111111111111111111111111                               

ช่อง 0 คอลัมน์ 2 ออฟเซ็ต 0x2d ความยาว 255 ความยาว (ทางกายภาพ) 255

varchar255 = 22222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222
22222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222
222222222222222222222222222222222222222222                               

ช่อง 0 คอลัมน์ 3 Offset 0x12c ความยาว 256 ความยาว (ทางกายภาพ) 256

varchar256 = 333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333
33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333
3333333333333333333333333333333333333333333                              

16

คนอื่น ๆ ได้ชี้ให้เห็นแล้วว่าจำนวนไบต์ที่จำเป็นในการจัดเก็บความยาวได้รับการแก้ไข ฉันต้องการเน้นส่วนนี้ในคำถามของคุณ:

มันสำคัญอีกต่อไปแล้ว ณ จุดนี้?

คุณมีคำถามของคุณแท็กด้วยรุ่นองค์กรซึ่งโดยทั่วไปหมายความว่าคุณมีข้อมูลจำนวนพอสมควร บ่อยครั้งที่ความแตกต่างของหนึ่งไบต์ต่อแถวจริงๆแล้วไม่สำคัญมากนักในทางปฏิบัติ ตัวอย่างเช่นตารางต่อไปนี้ที่มีVARCHAR(255)คอลัมน์เต็มจะใช้พื้นที่สูงสุด 143176 KB บนดิสก์:

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 ไม่มีผลกระทบต่อประสิทธิภาพ ข้อมูลอาจถูกจัดเก็บในรูปแบบ rowstore พร้อมการบีบอัดหน้าหรือการบีบอัดแถว ใช้เป็นแบบคอลัมน์แบบคอลัมน์หรือเป็นตารางที่ปรับให้เหมาะสมกับหน่วยความจำ แต่ละสิ่งเหล่านี้จะมีการแลกเปลี่ยนประสิทธิภาพแตกต่างกัน แต่ไม่สำคัญว่าคุณจะประกาศ varchar (255) หรือ varchar (256)


9
@ DavidBrowne-Microsoft no "ขนาดประกาศของ varchar ไม่มีผลกระทบต่อประสิทธิภาพ" ไม่เป็นความจริง - ขนาดของประเภทข้อมูลมีผลต่อหน่วยความจำที่ได้รับจากการสืบค้น ดูbrentozar.com/archive/2017/02/memory-grants-data-sizeสำหรับรายละเอียดเพิ่มเติม
เบรนต์โอซาร์

6
พยายามทำให้มันง่ายและกีดกันการเพิ่มประสิทธิภาพก่อนวัยอันควร
David Browne - Microsoft
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.