Ponieważ varchar
tak 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?
Ponieważ varchar
tak 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?
Odpowiedzi:
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_stats
kolumn wyjściowych, ale nadal powinna być możliwa do udowodnienia za pomocą SET STATISTICS TIME ON
lub 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:
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ć ):
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
).
varchar(450)
i varchar(255)
byłaby taka sama? (Lub coś poniżej 4000?)
rowcount*(column_size/2)
.