Varchar (5000) kullanmak varchar (255) ile kıyaslandığında kötü olur mu?


28

Yana varcharzaten dinamik alanı ayrılamadı, sorum kullanarak olup olmadığıdır varchar(255)daha verimli kullanılarak veya kıyasla daha fazla alan tasarrufu varchar(5000). Evet ise neden?


5000 karakter genişliğinde bir sütuna mı ihtiyacınız var? Öyleyse neden? Bir varchar (MAX) sütun burada sizin için daha iyi çalışır mı?
Richard L. Dawson,

Yanıtlar:


52

Evet, tüm değerler ikincisine uyacağından varchar(5000)daha kötü olabilir varchar(255). Bunun nedeni, SQL Server'ın veri boyutunu tahmin etmesi ve ardından bir tablodaki sütunların bildirilen ( gerçek olmayan ) boyutuna dayalı olarak bellek vermesidir . Sahip varchar(5000)olduğunuzda, her değerin 2500 karakter uzunluğunda olduğunu varsayar ve buna göre bellek ayırır.

İşte son GroupBy sunumumun kötü alışkanlıklarla ilgili sunumunu kendim için kanıtlamayı kolaylaştıran bir demo (bazı sys.dm_exec_query_statsçıktı sütunlarının SQL Server 2016 gerektirmesi gerekir, ancak yine de SET STATISTICS TIME ONönceki sürümlerdeki diğer araçlarla kanıtlanmalıdır ); aynı verilere karşı aynı sorgu için daha büyük bellek ve daha uzun çalışma süreleri gösterir - tek fark sütunların belirtilen boyutudur:

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

Yani, evet, sütunlarınızı doğru boyutlandırın , lütfen.

Ayrıca, testleri varchar (32), varchar (255), varchar (5000), varchar (8000) ve varchar (maks) ile yeniden yaptım. Benzer sonuçlar ( büyütmek için tıklayın ), 32 ile 255 arasındaki ve 5.000 ile 8.000 arasındaki farklar önemsizdi:

görüntü tanımını buraya girin

İşte TOP (5000)daha tam olarak tekrarlanabilen test için yapılan değişiklikle ilgili başka bir test: Sürekli olarak perişan oldum ( büyütmek için tıklayın ):

görüntü tanımını buraya girin

Dolayısıyla, 10.000 satır yerine 5.000 satır bile olsa (ve en azından SQL Server 2008 R2'ye kadar geriye doğru sys.all_columns'ta 5.000+ satır var), nispeten doğrusal bir ilerleme gözlenir - aynı verilerde bile, tanımlanan boyut büyüdükçe sütununda, aynı sorgunun yerine getirilmesi için daha fazla bellek ve zaman gerekir (anlamsız olsa bile DISTINCT).


bu gerçekten şaşırtıcı. Arasındaki fark ederim varchar(450)ve varchar(255)aynı olacak? (Ya da 4000'in altındaki bir şey?)
a_horse_with_no_name

@ a_horse_with_no_name Çalışma zamanı performansının tüm permütasyonlarını test etmedim, ancak bellek desteği doğrusal bir ilerleme olurdu - bu sadece bir fonksiyondur rowcount*(column_size/2).
Aaron Bertrand

O zaman çok hayal kırıklığı yarattı. Ancak, SQL Server'ın modern versiyonları bundan etkilenmiyor (tanımlı uzunluk 8000 veya belki 4000'den küçük olduğu sürece).
a_horse_with_no_name

1
@ a_horse_with_no_name Peki, verilerin dökülmeleri önlenmesi için ne kadar geniş olduğunu tahmin etmek zorunda. Başka nasıl tahmin etmeli? Tüm değişken genişlik sütunlarının avg / maks uzunluklarını bir yürütme planı oluşturmak için bir öncü olarak belirlemek için tüm tabloyu okuyamaz ve okuyamaz (ve yapabilse bile, bunu sadece bir yeniden derleme sırasında yapabilir).
Aaron Bertrand

2
Oracle, örneğin ortalama satır uzunluğu, her sütun için minimum ve maksimum değerler ile histogram hakkında istatistikler tutar. Postgres'ler çok benzer istatistikleri tutar (sıklıkta ama min / maks. Hiçbiri için, performansta nvarchar (150), nvarchar (2000) veya varchar (400) arasında herhangi bir fark yoktur.
a_horse_with_no_name
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.