Czy używanie varchar (5000) byłoby złe w porównaniu z varchar (255)?

28

Ponieważ varchartak czy inaczej przydzielaj przestrzeń dynamicznie, moje pytanie dotyczy tego, czy używanie varchar(255)jest bardziej wydajne, czy oszczędza więcej miejsca w porównaniu do używania varchar(5000). Jeśli tak, dlaczego?

Tintin
źródło
Czy potrzebujesz kolumny o szerokości 5000 znaków? Jeśli tak, DLACZEGO? Czy kolumna varchar (MAX) działałaby tutaj lepiej?
Richard L. Dawson

Odpowiedzi:

52

Tak, varchar(5000)może być gorzej, niż varchar(255)gdyby wszystkie wartości pasowały do ​​tego ostatniego. Powodem jest to, że SQL Server oszacuje rozmiar danych, a z kolei przydziały pamięci na podstawie zadeklarowanego (nie rzeczywistego ) rozmiaru kolumn w tabeli. Gdy varchar(5000)to zrobisz, przyjmie, że każda wartość ma 2500 znaków i na tej podstawie zarezerwuje pamięć.

Oto demonstracja z mojej ostatniej prezentacji GroupBy na temat złych nawyków, która ułatwia udowodnienie sobie (wymaga SQL Server 2016 dla niektórych sys.dm_exec_query_statskolumn wyjściowych, ale nadal powinna być możliwa do udowodnienia za pomocą SET STATISTICS TIME ONlub innych narzędzi we wcześniejszych wersjach); pokazuje większą pamięć i dłuższe środowiska wykonawcze dla tego samego zapytania dla tych samych danych - jedyną różnicą jest deklarowany rozmiar kolumn:

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

Tak , proszę odpowiednio dobrać kolumny .

Ponownie uruchomiłem testy z varchar (32), varchar (255), varchar (5000), varchar (8000) i varchar (max). Podobne wyniki ( kliknij, aby powiększyć ), choć różnice między 32 a 255 oraz między 5000 a 8000 były nieistotne:

wprowadź opis zdjęcia tutaj

Oto kolejny test ze TOP (5000)zmianą dla bardziej w pełni powtarzalnego testu, o który byłem nieustannie nękany ( kliknij, aby powiększyć ):

wprowadź opis zdjęcia tutaj

Więc nawet przy 5000 wierszy zamiast 10.000 wierszy (i jest ponad 5000 wierszy w sys.all_columns co najmniej tak daleko jak SQL Server 2008 R2), obserwuje się względnie liniowy postęp - nawet przy tych samych danych, im większy jest zdefiniowany rozmiar kolumny, tym więcej pamięci i czasu jest potrzebnych do spełnienia dokładnie tego samego zapytania (nawet jeśli nie ma ono sensu DISTINCT).

Aaron Bertrand
źródło
to jest naprawdę zaskakujące. Czy różnica między varchar(450)i varchar(255)byłaby taka sama? (Lub coś poniżej 4000?)
a_horse_w_no_name
@ a_horse_with_no_name Nie przetestowałem wszystkich permutacji wydajności środowiska wykonawczego, ale przyznanie pamięci będzie postępem liniowym - jest to po prostu funkcja rowcount*(column_size/2).
Aaron Bertrand
To dość rozczarowujące. Myślałem, że współczesne wersje SQL Server nie cierpią z tego powodu (o ile zdefiniowana długość jest mniejsza niż 8000 lub może 4000).
a_horse_w_no_name
1
@ a_horse_with_no_name Cóż, musi zgadywać, jak szerokie są dane, aby uniknąć rozlania. Jak inaczej powinien zgadywać? Nie może skanować i czytać całej tabeli, aby określić średnie / maksymalne długości wszystkich kolumn o zmiennej szerokości jako prekursor do generowania planu wykonania (a nawet gdyby mógł, byłby w stanie to zrobić tylko podczas ponownej kompilacji).
Aaron Bertrand
2
Oracle prowadzi statystyki dotyczące np. Średniej długości wiersza, wartości minimalnej i maksymalnej dla każdej kolumny, a także histogramu. Postgres prowadzi bardzo podobne statystyki (choć nie rejestruje min / maks, ale częstotliwości). W przypadku żadnego z nich nie ma żadnej różnicy między wydajnością nvarchar (150), nvarchar (2000) lub varchar (400).
a_horse_w_no_name