Apakah menggunakan varchar (5000) lebih buruk dibandingkan dengan varchar (255)?


28

Karena bagaimanapun varcharmengalokasikan ruang secara dinamis, pertanyaan saya adalah apakah menggunakan varchar(255)lebih efisien atau menghemat lebih banyak ruang daripada menggunakan varchar(5000). Jika ya, mengapa?


Apakah Anda memerlukan kolom lebar 5.000 karakter? Jika demikian MENGAPA? Apakah kolom varchar (MAX) berfungsi lebih baik untuk Anda di sini?
Richard L. Dawson

Jawaban:


52

Ya, varchar(5000)bisa lebih buruk daripada varchar(255)jika semua nilai cocok dengan yang terakhir. Alasannya adalah bahwa SQL Server akan memperkirakan ukuran data dan, pada gilirannya, hibah memori berdasarkan ukuran kolom yang dinyatakan (tidak aktual ) dalam sebuah tabel. Jika sudah varchar(5000), Anda akan menganggap bahwa setiap nilai sepanjang 2.500 karakter, dan mencadangkan memori berdasarkan itu.

Berikut ini adalah demo dari presentasi GroupBy saya baru-baru ini tentang kebiasaan buruk yang membuatnya mudah untuk dibuktikan sendiri (memerlukan SQL Server 2016 untuk beberapa sys.dm_exec_query_statskolom output, tetapi masih harus dapat dibuktikan dengan SET STATISTICS TIME ONatau alat lain pada versi sebelumnya); itu menunjukkan memori lebih besar dan runtimes lebih lama untuk permintaan yang sama terhadap data yang sama - satu-satunya perbedaan adalah ukuran kolom yang dinyatakan:

-- create three tables with different column sizes
CREATE TABLE dbo.t1(a nvarchar(32),   b nvarchar(32),   c nvarchar(32),   d nvarchar(32));
CREATE TABLE dbo.t2(a nvarchar(4000), b nvarchar(4000), c nvarchar(4000), d nvarchar(4000));
CREATE TABLE dbo.t3(a nvarchar(max),  b nvarchar(max),  c nvarchar(max),  d nvarchar(max));
GO -- that's important

-- Method of sample data pop : irrelevant and unimportant.
INSERT dbo.t1(a,b,c,d)
  SELECT TOP (5000) LEFT(name,1), RIGHT(name,1), ABS(column_id/10), ABS(column_id%10)
  FROM sys.all_columns ORDER BY object_id;
GO 100
INSERT dbo.t2(a,b,c,d) SELECT a,b,c,d FROM dbo.t1;
INSERT dbo.t3(a,b,c,d) SELECT a,b,c,d FROM dbo.t1;
GO

-- no "primed the cache in advance" tricks
DBCC FREEPROCCACHE WITH NO_INFOMSGS;
DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;
GO

-- Redundancy in query doesn't matter! Just has to create need for sorts etc.
GO
SELECT DISTINCT a,b,c,d, DENSE_RANK() OVER (PARTITION BY b,c ORDER BY d DESC)
FROM dbo.t1 GROUP BY a,b,c,d ORDER BY c,a DESC;
GO
SELECT DISTINCT a,b,c,d, DENSE_RANK() OVER (PARTITION BY b,c ORDER BY d DESC)
FROM dbo.t2 GROUP BY a,b,c,d ORDER BY c,a DESC;
GO
SELECT DISTINCT a,b,c,d, DENSE_RANK() OVER (PARTITION BY b,c ORDER BY d DESC)
FROM dbo.t3 GROUP BY a,b,c,d ORDER BY c,a DESC;
GO

SELECT [table] = N'...' + SUBSTRING(t.[text], CHARINDEX(N'FROM ', t.[text]), 12) + N'...', 
s.last_dop, s.last_elapsed_time, s.last_grant_kb, s.max_ideal_grant_kb
FROM sys.dm_exec_query_stats AS s CROSS APPLY sys.dm_exec_sql_text(s.sql_handle) AS t
WHERE t.[text] LIKE N'%dbo.'+N't[1-3]%' ORDER BY t.[text];

Jadi, ya, ukuran kolom Anda benar , tolong.

Juga, saya menjalankan kembali tes dengan varchar (32), varchar (255), varchar (5000), varchar (8000), dan varchar (maks). Hasil serupa ( klik untuk memperbesar ), meskipun perbedaan antara 32 dan 255, dan antara 5.000 dan 8.000, dapat diabaikan:

masukkan deskripsi gambar di sini

Berikut ini adalah tes lain dengan TOP (5000)perubahan untuk tes yang sepenuhnya dapat direproduksi yang sedang saya ganggu terus-menerus ( klik untuk memperbesar ):

masukkan deskripsi gambar di sini

Jadi, bahkan dengan 5.000 baris, bukan 10.000 baris (dan ada 5.000+ baris di sys.all_columns setidaknya sejauh SQL Server 2008 R2), perkembangan yang relatif linier diamati - bahkan dengan data yang sama, semakin besar ukuran yang ditentukan dari kolom, semakin banyak memori dan waktu yang diperlukan untuk memenuhi permintaan yang sama persis (bahkan jika itu tidak memiliki arti DISTINCT).


ini sangat mengejutkan. Apakah perbedaan antara varchar(450)dan varchar(255)sama? (Atau apa pun di bawah 4000?)
a_horse_with_no_name

@ a_horse_with_no_name Saya belum menguji semua permutasi dari kinerja runtime, tetapi hibah memori akan menjadi perkembangan linier - itu hanyalah fungsi dari rowcount*(column_size/2).
Aaron Bertrand

Itu cukup mengecewakan. Saya berpikir bahwa versi modern SQL Server tidak menderita dari itu (selama panjang yang ditentukan lebih kecil dari 8000 atau mungkin 4000).
a_horse_with_no_name

1
@a_horse_with_no_name Ya, harus membuat tebakan seberapa lebar data sehingga menghindari tumpahan. Bagaimana lagi yang bisa ditebak? Itu tidak dapat memindai dan membaca seluruh tabel untuk menentukan panjang rata-rata / maksimum semua kolom lebar variabel sebagai prekursor untuk menghasilkan rencana eksekusi (dan bahkan jika itu bisa, itu hanya akan dapat melakukan itu selama kompilasi ulang).
Aaron Bertrand

2
Oracle menyimpan statistik tentang mis. Panjang baris rata-rata, nilai min dan maks untuk setiap kolom serta histogram. Postgres menyimpan statistik yang sangat mirip (meskipun tidak mencatat min / max tetapi frekuensi). Bagi mereka tidak ada perbedaan apa pun antara nvarchar (150), nvarchar (2000) atau varchar (400) dalam kinerja.
a_horse_with_no_name
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.