varchar (255) atau varchar (256)?

21

Haruskah saya menggunakan varchar(255)atau varchar(256)ketika mendesain tabel? Saya pernah mendengar satu byte digunakan untuk panjang kolom, atau untuk menyimpan metadata.

Apakah itu penting lagi pada saat ini?

Saya melihat beberapa posting di internet, tetapi mereka berlaku untuk Oracle dan MySQL.

Kami memiliki Microsoft SQL Server 2016 Enterprise Edition, bagaimana itu berlaku untuk lingkungan ini?

Sekarang katakan misalnya, bagaimana jika saya memberi tahu klien saya untuk menyimpan misalnya, deskripsi teks ke 255 karakter, bukan 256, apakah ada perbedaan? Apa yang saya baca "Dengan panjang maksimal 255 karakter, DBMS dapat memilih untuk menggunakan satu byte untuk menunjukkan panjang data di lapangan. Jika batasnya 256 atau lebih besar, diperlukan dua byte." Apakah ini benar?


sumber
FYI: pertanyaan ini diposting di forum MSDN: social.msdn.microsoft.com/Forums/sqlserver/en-US/…
Solomon Rutzky

Jawaban:

36

Ukuran masing-masing dan setiap kolom dengan tepat. JANGAN gunakan ukuran "standar" untuk setiap kolom. Jika Anda hanya membutuhkan 30 karakter, mengapa membuat kolom yang dapat menangani 255? Saya sangat senang Anda tidak menganjurkan penggunaan varchar(max)untuk kolom string Anda.

Ini adalah saran yang sangat bijaksana jika Anda perlu mengindeks kolom, atau jika Anda menggunakan kolom sebagai kunci utama dan memiliki referensi kunci asing. SQL Server menggunakan ukuran setiap kolom dalam pengoptimal kueri untuk memahami perkiraan kebutuhan memori untuk pemrosesan kueri. Memiliki kolom yang terlalu besar dapat merusak kinerja.

Indeks pada kolom yang terlalu besar dapat menyebabkan kesalahan yang dihasilkan:

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

Upaya untuk membuat indeks di atas menghasilkan peringatan ini:

Peringatan! Panjang kunci maksimum adalah 900 byte. Indeks 'IX_WideIndex_01' memiliki panjang maksimum 1110 byte. Untuk beberapa kombinasi nilai besar, operasi penyisipan / pembaruan akan gagal.

900 byte adalah ukuran kunci maksimum untuk indeks berkerumun (dan indeks non-berkerumun di SQL Server 2012 dan lebih lama). 1700 byte adalah ukuran kunci maksimum untuk indeks yang tidak berkerumun pada versi SQL Server yang lebih baru. Jika Anda mendesain kolom dengan lebar umum, seperti (255), Anda mungkin mengalami peringatan ini jauh lebih sering dari yang diharapkan.

Jika Anda tertarik dengan penyimpanan internal, Anda dapat menggunakan tes kecil berikut untuk lebih memahami bagaimana SQL Server menyimpan data baris-toko terkompresi.

Pertama, kami akan membuat tabel tempat kami dapat menyimpan kolom dengan berbagai ukuran:

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

Sekarang kita akan memasukkan satu baris:

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

Kueri ini menggunakan fungsi tidak terdokumentasi, dan tidak didukung, sys.fn_RowDumpCrackerdan sys.fn_PhyslocCrackeruntuk menampilkan beberapa detail menarik tentang tabel:

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

Output akan terlihat mirip dengan ini:

╔═════════════════════╦════════════╦═════════╦════ ══════╦══════════════════════════╦══════════╦═════ ════════╦═════════════╦═════════╦═════════╦═══════ ══╗
║ partition_id ║ colName ║ IsInrow ║ IsSparse ║ IsRecordPrefixCompressed ║ IsSymbol ║ PrefixBytes ║ InRowLength ║ file_id ║ page_id ║ page_id ║ slot_id ║
╠═════════════════════╬════════════╬═════════╬════ ══════╬══════════════════════════╬══════════╬═════ ════════╬═════════════╬═════════╬═════════╬═══════ ══╣
║ 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 ║
╚═════════════════════╩════════════╩═════════╩════ ══════╩══════════════════════════╩══════════╩═════ ════════╩═════════════╩═════════╩═════════╩═══════ ══╝

Seperti yang Anda lihat, nilai InRowLengthmasing-masing ditampilkan, bersama dengan lokasi penyimpanan fisik setiap baris - "file_id", "page_id", dan "slot_id".

Jika kita mengambil file_iddan page_idmenghargai dari hasil kueri di atas dan menjalankannya DBCC PAGE, kita dapat melihat konten halaman fisik yang sebenarnya:

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

Hasil dari mesin saya adalah:

HALAMAN: (1: 1912)


PENYANGGA:


BUF @ 0x00000000FF5B2E80

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

KEPALA HALAMAN:


Halaman @ 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
Metadata: AllocUnitId = 2233785421652951040                              
Metadata: PartitionId = 1945555045333008384 Metadata: IndexId = 0
Metadata: 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                      

Status Alokasi

GAM (1: 2) = ALOKASI SGAM (1: 3) = BUKAN ALOKASI (1: 1) = 0x41 ALOKASI 50_PCT_FULL
DIFF (1: 6) = TIDAK BERUBAH ML (1: 7) = TIDAK MIN_LOGGED           

Slot 0 Offset 0x60 Panjang 556

Jenis Catatan = PRIMARY_RECORD Atribut Catatan = NULL_BITMAP VARIABLE_COLUMNS
Ukuran Rekaman = 556                   
Memory Dump @ 0x000000005145A060

0000000000000000: 30000400 03000003 002d002c 012c0231 31313131 0 ........-.,.,. 11111
0000000000000014: 31313131 31313131 31313131 31313131 31313131 1111111111111111111111
000000000000002828: 31313131 31323232 32323232 32323232 32323232 111112222222222222222222
000000000000003C: 32323232 32323232 32323232 32323232 32323232 22222222222222222222222222
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 22222222222222222222222222
00000000000000A0: 32323232 32323232 32323232 32323232 32323232 22222222222222222222222222
00000000000000B4: 32323232 32323232 32323232 32323232 32323232 22222222222222222222222222
00000000000000C8: 32323232 32323232 32323232 32323232 32323232 22222222222222222222222222
00000000000000DC: 32323232 32323232 32323232 32323232 32323232 222222222222222222222222
00000000000000F0: 32323232 32323232 32323232 32323232 32323232 22222222222222222222222222
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 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
00000000000001CC: 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
000000000000021C: 33333333 33333333 33333333 33333333 3333333333333333

Slot 0 Kolom 1 Offset 0xf Panjang 30 Panjang (fisik) 30

varchar30 = 111111111111111111111111111111                               

Slot 0 Kolom 2 Offset 0x2d Panjang 255 Panjang (fisik) 255

varchar255 = 2222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222
222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222
222222222222222222222222222222222222222222                               

Slot 0 Kolom 3 Offset 0x12c Panjang 256 Panjang (fisik) 256

varchar256 = 333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333
3333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333
33333333333333333333333333333333333333333                              
Max Vernon
sumber
16

Yang lain telah menunjukkan bahwa jumlah byte yang diperlukan untuk menyimpan panjangnya telah diperbaiki. Saya ingin fokus pada bagian ini dalam pertanyaan Anda:

Apakah itu penting lagi pada saat ini?

Anda memiliki pertanyaan yang ditandai dengan edisi perusahaan, yang umumnya berarti Anda memiliki cukup banyak data. Seringkali perbedaan satu byte per baris benar-benar tidak terlalu penting dalam praktiknya. Misalnya, tabel berikut dengan VARCHAR(255)kolom yang terisi penuh membutuhkan ruang 143176 KB pada disk:

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

Hasil:

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

Mari kita buat tabel kedua dengan VARCHAR(256)kolom yang terisi penuh . Itu akan mengambil setidaknya satu byte lagi per baris, kan?

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

Hasil:

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

Kebetulan kedua tabel mengambil jumlah ruang yang sama. Jumlah baris yang sama cocok untuk setiap halaman 8k. Sangat menyenangkan bahwa Anda ingin menghabiskan waktu untuk mengoptimalkan aplikasi Anda, tetapi saya curiga Anda lebih baik fokus pada bidang yang berbeda.

Joe Obbish
sumber
7

Ukuran varchar yang dideklarasikan tidak memiliki dampak kinerja. Data mungkin sebenarnya disimpan sebagai rowstore dengan kompresi halaman atau kompresi baris. Sebagai Clumned Columnstore, atau sebagai tabel Dioptimalkan-Memori. Masing-masing akan memiliki trade-off kinerja yang berbeda, tetapi tidak masalah apakah Anda mendeklarasikan varchar (255) atau varchar (256).

David Browne - Microsoft
sumber
9
@ DavidBrowne-Microsoft no, "menyatakan ukuran varchar tidak memiliki dampak kinerja" jelas tidak benar - ukuran datatype memengaruhi hibah memori untuk kueri. Lihat brentozar.com/archive/2017/02/memory-grants-data-size untuk detail lebih lanjut.
Brent Ozar
6
Mencoba membuatnya tetap sederhana, dan mencegah optimasi prematur.
David Browne - Microsoft