varchar (255) hay varchar (256)?


21

Tôi nên sử dụng varchar(255)hoặc varchar(256)khi thiết kế bảng? Tôi đã nghe nói một byte được sử dụng cho chiều dài của cột hoặc để lưu trữ siêu dữ liệu.

Nó có quan trọng nữa vào thời điểm này?

Tôi thấy một số bài đăng trên internet, tuy nhiên chúng áp dụng cho Oracle và MySQL.

Chúng tôi có Microsoft SQL Server 2016 Enterprise Edition, nó áp dụng cho môi trường này như thế nào?

Bây giờ hãy nói ví dụ, nếu tôi bảo khách hàng của mình giữ ví dụ, mô tả văn bản thành 255 ký tự thay vì 256, thì có sự khác biệt nào không? Những gì tôi đọc được "Với độ dài tối đa 255 ký tự, DBMS có thể chọn sử dụng một byte đơn để chỉ ra độ dài của dữ liệu trong trường. Nếu giới hạn là 256 hoặc lớn hơn, sẽ cần hai byte." Điều này có đúng không?


FYI: câu hỏi này đã được đăng chéo trên các diễn đàn MSDN: social.msdn.microsoft.com/Forums/sqlserver/en-US/ Kẻ
Solomon Rutzky

Câu trả lời:


36

Kích thước mỗi cột một cách thích hợp. KHÔNG sử dụng kích thước "tiêu chuẩn" cho mỗi cột. Nếu bạn chỉ cần 30 ký tự, tại sao tạo một cột có thể xử lý 255? Tôi rất vui vì bạn không ủng hộ việc sử dụng varchar(max)cho các cột chuỗi của bạn.

Đây là lời khuyên đặc biệt thận trọng nếu bạn cần lập chỉ mục một cột hoặc nếu bạn đang sử dụng một cột làm khóa chính và nó có các tham chiếu khóa ngoài. SQL Server sử dụng kích thước của mỗi cột trong trình tối ưu hóa truy vấn để hiểu các yêu cầu bộ nhớ ước tính để xử lý truy vấn. Có các cột quá khổ có thể gây bất lợi cho hiệu suất.

Các chỉ mục trên các cột quá khổ có thể dẫn đến lỗi được tạo:

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);

Nỗ lực tạo chỉ mục trên cho kết quả trong cảnh báo này:

Cảnh báo! Độ dài khóa tối đa là 900 byte. Chỉ mục 'IX_WideIndex_01' có độ dài tối đa 1110 byte. Đối với một số kết hợp của các giá trị lớn, thao tác chèn / cập nhật sẽ thất bại.

900 byte là kích thước khóa tối đa cho các chỉ mục được phân cụm (và các chỉ mục không được phân cụm trên SQL Server 2012 trở lên). 1700 byte là kích thước khóa tối đa cho các chỉ mục không được nhóm trên các phiên bản SQL Server mới hơn. Nếu bạn thiết kế các cột có chiều rộng chung, chẳng hạn như (255), bạn có thể gặp cảnh báo này thường xuyên hơn dự kiến.

Trong trường hợp bạn quan tâm đến nội bộ lưu trữ, bạn có thể sử dụng thử nghiệm nhỏ sau đây để hiểu rõ hơn về cách SQL Server lưu trữ dữ liệu lưu trữ hàng không nén.

Trước tiên, chúng tôi sẽ tạo một bảng nơi chúng tôi có thể lưu trữ các cột có kích thước khác nhau:

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
);

Bây giờ chúng ta sẽ chèn một hàng duy nhất:

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

Truy vấn này sử dụng các hàm không có giấy tờ và không được hỗ trợ sys.fn_RowDumpCrackersys.fn_PhyslocCrackerđể hiển thị một số chi tiết thú vị về bảng:

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

Đầu ra sẽ trông tương tự như thế này:

╔═════════════════════╦════════════╦═════════╦════ ══════╦══════════════════════════╦══════════╦═════ ════════╦═════════════╦═════════╦═════════╦═══════ ══╗
║ phân vùng_id colName IsInrow IsSparse IsRecordPrefixCompced ║ IsSymbol PrefixBytes InRowLpm ║ 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
╚═════════════════════╩════════════╩═════════╩════ ══════╩══════════════════════════╩══════════╩═════ ════════╩═════════════╩═════════╩═════════╩═══════ ══╝

Như bạn có thể thấy, InRowLengthcho mỗi giá trị được hiển thị, cùng với vị trí lưu trữ vật lý của mỗi hàng - "file_id", "page_id" và "slot_id".

Nếu chúng tôi lấy file_idpage_idcác giá trị từ các kết quả truy vấn ở trên và chạy DBCC PAGEvới chúng, chúng tôi có thể thấy nội dung trang vật lý thực tế:

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

Kết quả từ máy của tôi là:

TRANG: (1: 1912)


ĐỆM:


BUF @ 0x00000000FF5B2E80

bpage = 0x0000000024130000 bhash = 0x0000000000000000 bpageno = (1: 1912)
bdbid = 2 breferences = 0 bcputicks = 0
bsampleCount = 0 bUse1 = 32497 bstat = 0x10b
blog = 0x212121cc bnext = 0x00000000000000000000          

TRANG CHỦ ĐẦU TƯ:


Trang @ 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
Siêu dữ liệu: AllocUnitId = 2233785421652951040                              
Siêu dữ liệu: Phân vùngId = 1945555045333008384 Siêu dữ liệu: IndexId = 0
Siêu dữ liệu: 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                      

Tình trạng phân bổ

GAM (1: 2) = SGAM ALLOCATED (1: 3) = PFS KHÔNG ĐƯỢC PHÉP (1: 1) = 0x41 ALLOCATED 50_PCT_FULL
DIFF (1: 6) = KHÔNG THAY ĐỔI ML (1: 7) = KHÔNG MIN_LOGGED           

Khe 0 Offset 0x60 Chiều dài 556

Loại bản ghi = PRIMARY_RECORD Thuộc tính bản ghi = NULL_BITMAP VARIABLE_COLUMNS
Kích thước bản ghi = 556                   
Bộ nhớ kết xuất @ 0x000000005145A060

0000000000000000: 30000400 03000003 002d002c 012c0231 31313131 0 ........-.,.,. 111111
0000000000000014: 31313131 31313131 31313131 31313131 31313131 1111111111111111111111
0000000000000028: 31313131 31323232 32323232 32323232 32323232 111112222222222222222222
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
00000000000000A0: 32323232 32323232 32323232 32323232 32323232 222222222222222222222222
00000000000000B4: 32323232 32323232 32323232 32323232 32323232 222222222222222222222222
00000000000000C8: 32323232 32323232 32323232 32323232 32323232 222222222222222222222222
00000000000000DC: 32323232 32323232 32323232 32323232 32323232 222222222222222222222222
00000000000000F0: 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 33333333333333333333333
0000000000000140: 33333333 33333333 33333333 33333333 33333333 33333333333333333333333
0000000000000154: 33333333 33333333 33333333 33333333 33333333 33333333333333333333333
0000000000000168: 33333333 33333333 33333333 33333333 33333333 33333333333333333333333
000000000000017C: 33333333 33333333 33333333 33333333 33333333 33333333333333333333333
0000000000000190: 33333333 33333333 33333333 33333333 33333333 33333333333333333333333
00000000000001A4: 33333333 33333333 33333333 33333333 33333333 3333333333333333333333
00000000000001B8: 33333333 33333333 33333333 33333333 33333333 33333333333333333333333
00000000000001CC: 33333333 33333333 33333333 33333333 33333333 33333333333333333333333
00000000000001E0: 33333333 33333333 33333333 33333333 33333333 3333333333333333333333
00000000000001F4: 33333333 33333333 33333333 33333333 33333333 33333333333333333333333
0000000000000208: 33333333 33333333 33333333 33333333 33333333 33333333333333333333
000000000000021C: 33333333 33333333 33333333 33333333 3333333333333333333

Khe 0 Cột 1 Offset 0xf Chiều dài 30 Chiều dài (vật lý) 30

varar30 = 11111111111111111111111111111111                               

Khe 0 Cột 2 Offset 0x2d Chiều dài 255 Chiều dài (vật lý) 255

varchar255 = 22222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222
22222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222
222222222222222222222222222222222222222222222222                               

Khe 0 Cột 3 Offset 0x12c Chiều dài 256 Chiều dài (vật lý) 256

varchar256 = 33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333
3333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333
333333333333333333333333333333333333333333333                              

16

Những người khác đã chỉ ra rằng số lượng byte cần thiết để lưu trữ độ dài là cố định. Tôi muốn tập trung vào phần này trong câu hỏi của bạn:

Nó có quan trọng nữa vào thời điểm này?

Bạn có câu hỏi của bạn được gắn thẻ với phiên bản doanh nghiệp, điều đó thường có nghĩa là bạn sẽ có một lượng dữ liệu hợp lý. Thông thường sự khác biệt của một byte trên mỗi hàng thực sự không quá quan trọng trong thực tế. Ví dụ: bảng sau với VARCHAR(255)cột được điền đầy đủ chiếm không gian 143176 KB trên đĩa:

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';

Các kết quả:

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

Hãy tạo một bảng thứ hai với một VARCHAR(256)cột được điền đầy đủ . Điều đó sẽ mất ít nhất một byte mỗi hàng, phải không?

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';

Các kết quả:

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

Nó chỉ xảy ra khi cả hai bảng chiếm cùng một không gian. Số lượng hàng giống nhau phù hợp trên mỗi trang 8k. Thật tuyệt khi bạn muốn dành thời gian tối ưu hóa ứng dụng của mình, nhưng tôi nghi ngờ rằng bạn nên tập trung vào các lĩnh vực khác nhau.


7

Kích thước khai báo của varchar không có tác động hiệu suất. Dữ liệu có thể thực sự được lưu trữ dưới dạng kho lưu trữ với nén trang hoặc nén hàng. Là một Cột phân cụm hoặc dưới dạng bảng Tối ưu hóa bộ nhớ. Mỗi trong số này sẽ có sự đánh đổi hiệu suất khác nhau, nhưng nó không bao giờ quan trọng cho dù bạn khai báo một varchar (255) hay varchar (256).


9
@ DavidBrowne-Microsoft không, "kích thước khai báo của varchar không có tác động hiệu suất" chắc chắn là không đúng - kích thước kiểu dữ liệu ảnh hưởng đến việc cấp bộ nhớ cho các truy vấn. Xem brentozar.com/archive/2017/02/memory-grant-data-size để biết thêm chi tiết.
Brent Ozar

6
Cố gắng giữ cho nó đơn giản, và không khuyến khích tối ưu hóa sớm.
David Browne - Microsoft
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.