L'utilisation de varchar (5000) serait-elle mauvaise par rapport à varchar (255)?

28

Puisque de varchartoute façon allouer de l'espace de manière dynamique, ma question est de savoir si l'utilisation varchar(255)est plus efficace ou économise plus d'espace que l'utilisation varchar(5000). Si oui, pourquoi?

Tintin
la source
Avez-vous besoin d'une colonne de 5000 caractères? Si oui, pourquoi? Une colonne varchar (MAX) fonctionnerait-elle mieux pour vous ici?
Richard L. Dawson,

Réponses:

52

Oui, cela varchar(5000)peut être pire que varchar(255)si toutes les valeurs rentrent dans ce dernier. La raison en est que SQL Server estimera la taille des données et, à son tour, les allocations de mémoire en fonction de la taille déclarée (non réelle ) des colonnes d'une table. Lorsque vous l'avez varchar(5000), il supposera que chaque valeur comporte 2 500 caractères et réservera de la mémoire en fonction de cela.

Voici une démo de ma récente présentation GroupBy sur les mauvaises habitudes qui facilite la preuve par vous-même (nécessite SQL Server 2016 pour certaines des sys.dm_exec_query_statscolonnes de sortie, mais devrait toujours être prouvable avec SET STATISTICS TIME ONou d'autres outils sur les versions antérieures); il montre une plus grande mémoire et des durées d'exécution plus longues pour la même requête par rapport aux mêmes données - la seule différence est la taille déclarée des colonnes:

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

Alors, oui, dimensionnez correctement vos colonnes , s'il vous plaît.

De plus, j'ai relancé les tests avec varchar (32), varchar (255), varchar (5000), varchar (8000) et varchar (max). Des résultats similaires ( cliquez pour agrandir ), bien que les différences entre 32 et 255, et entre 5 000 et 8 000, étaient négligeables:

entrez la description de l'image ici

Voici un autre test avec le TOP (5000)changement pour le test plus entièrement reproductible qui me harcelait sans cesse ( cliquez pour agrandir ):

entrez la description de l'image ici

Ainsi, même avec 5000 lignes au lieu de 10000 lignes (et il y a plus de 5000 lignes dans sys.all_columns au moins aussi loin que SQL Server 2008 R2), une progression relativement linéaire est observée - même avec les mêmes données, plus la taille définie est grande de la colonne, plus de mémoire et de temps sont nécessaires pour satisfaire exactement la même requête (même si elle n'a pas de sens DISTINCT).

Aaron Bertrand
la source
c'est vraiment surprenant. La différence entre varchar(450)et varchar(255)serait-elle la même? (Ou quelque chose en dessous de 4000?)
a_horse_with_no_name
@a_horse_with_no_name Je n'ai pas testé toutes les permutations des performances d'exécution, mais l'attribution de mémoire serait une progression linéaire - c'est simplement une fonction de rowcount*(column_size/2).
Aaron Bertrand
C'est assez décevant alors. Je pense que les versions modernes de SQL Server ne souffrent pas de cela (tant que la longueur définie est inférieure à 8000 ou peut-être 4000).
a_horse_with_no_name
1
@a_horse_with_no_name Eh bien, il doit faire une estimation de la largeur des données afin d'éviter les déversements. Sinon, comment deviner? Il ne peut pas analyser et lire l'intégralité du tableau pour déterminer les longueurs moyenne / maximale de toutes les colonnes de largeur variable en tant que précurseur de la génération d'un plan d'exécution (et même s'il le pouvait, il ne pourrait le faire que lors d'une recompilation).
Aaron Bertrand
2
Oracle conserve par exemple des statistiques sur la longueur moyenne des lignes, les valeurs min et max pour chaque colonne ainsi qu'un histogramme. Postgres conserve des statistiques très similaires (il n'enregistre pas de min / max mais des fréquences). Pour aucun d'eux, il n'y a une quelconque différence entre nvarchar (150), nvarchar (2000) ou varchar (400) dans les performances.
a_horse_with_no_name