Было бы плохо использовать varchar (5000) по сравнению с varchar (255)?

28

Так как в varcharлюбом случае динамически выделяется пространство, мой вопрос заключается в том, varchar(255)будет ли использование более эффективным или сэкономит больше места по сравнению с использованием varchar(5000). Если да, то почему?

Тинтин
источник
Вам нужен столбец шириной 5000 символов? Если так, ПОЧЕМУ? Будет ли колонна varchar (MAX) работать лучше для вас здесь?
Ричард Доусон

Ответы:

52

Да, varchar(5000)может быть хуже, чем varchar(255)если бы все значения вписывались в последние. Причина в том, что SQL Server будет оценивать размер данных и, в свою очередь, предоставление памяти на основе объявленного (не фактического ) размера столбцов в таблице. Если у вас есть varchar(5000), он будет предполагать, что каждое значение имеет длину 2500 символов, и резервировать память на основе этого.

Вот демонстрация из моей недавней презентации GroupBy о вредных привычках, которая позволяет легко доказать это для себя (требуется SQL Server 2016 для некоторых sys.dm_exec_query_statsвыходных столбцов, но он все же должен быть подтвержден с помощью SET STATISTICS TIME ONдругих инструментов в более ранних версиях); он показывает больший объем памяти и более длительное время выполнения для одного и того же запроса к тем же данным - единственное отличие заключается в объявленном размере столбцов:

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

Так что, да , пожалуйста, измените размер столбцов .

Кроме того, я перезапустил тесты с varchar (32), varchar (255), varchar (5000), varchar (8000) и varchar (max). Аналогичные результаты ( нажмите, чтобы увеличить ), хотя различия между 32 и 255 и между 5000 и 8000, были незначительными:

введите описание изображения здесь

Вот еще один тест с TOP (5000)изменением более полностью воспроизводимого теста, о котором меня постоянно преследовали ( нажмите, чтобы увеличить ):

введите описание изображения здесь

Таким образом, даже с 5000 строк вместо 10000 строк (и в sys.all_columns более 5000 строк, по крайней мере, так же далеко, как в SQL Server 2008 R2), наблюдается относительно линейная прогрессия - даже с теми же данными, чем больше определенный размер столбца, тем больше памяти и времени требуется для выполнения точно такого же запроса (даже если он имеет бессмысленный смысл DISTINCT).

Аарон Бертран
источник
это действительно удивительно. Будет ли разница между varchar(450)и varchar(255)быть одинаковой? (Или что-нибудь ниже 4000?)
a_horse_with_no_name
@a_horse_with_no_name Я не проверял все перестановки производительности во время выполнения, но предоставление памяти было бы линейной прогрессией - это просто функция rowcount*(column_size/2).
Аарон Бертран
Это довольно разочаровывает тогда. Я думаю, что современные версии SQL Server не страдают от этого (до тех пор, пока определенная длина меньше 8000 или, может быть, 4000).
a_horse_with_no_name
1
@a_horse_with_no_name Ну, нужно угадать, насколько широки данные, чтобы избежать разливов. Как еще это должно угадать? Он не может сканировать и читать всю таблицу, чтобы определить среднюю / среднюю длину всех столбцов переменной ширины в качестве предвестника для создания плана выполнения (и даже если бы он мог, он мог бы сделать это только во время перекомпиляции).
Аарон Бертран
2
Oracle хранит статистику, например, о средней длине строки, минимальном и максимальном значениях для каждого столбца, а также гистограмме. Postgres ведет очень похожую статистику (хотя он и не записывает мин / макс, но частоты). Ни для одного из них нет никакой разницы между производительностью nvarchar (150), nvarchar (2000) или varchar (400).
a_horse_with_no_name