Чи погано використання 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];

Отже, так , будь ласка, розміруйте свої колонки потрібного розміру .

Також я повторно провів тести з варчаром (32), варчаром (255), варчаром (5000), варчаром (8000) та варчаром (макс.). Подібні результати ( натисніть, щоб збільшити ), хоча різниці між 32 і 255, і між 5000 і 8000, були незначними:

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

Ось ще один тест зі TOP (5000)зміною для більш повністю відтворюваного тесту, про який я невпинно бракував ( натисніть, щоб збільшити ):

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

Тож навіть при 5000 рядках замість 10 000 рядків (а в 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 Добре, він повинен здогадатися, наскільки широкі дані, щоб уникнути розливу. Як ще слід здогадуватися? Він не може сканувати та зчитувати всю таблицю, щоб визначити avg / max довжини всіх стовпців змінної ширини як попередник генерування плану виконання (і навіть якщо це могло, це можна було б зробити лише під час перекомпіляції).
Аарон Бертран

2
Oracle веде статистику, наприклад, про середню довжину рядка, значення min та max для кожного стовпця, а також гістограму. Postgres веде дуже схожу статистику (він не записує мінімум / макс, але частоти). Для жодного з них немає різниці між nvarchar (150), nvarchar (2000) або varchar (400) у виконанні.
a_horse_with_no_name
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.