¿Usar varchar (5000) sería malo en comparación con varchar (255)?

28

Como de varchartodos modos asigna espacio dinámicamente, mi pregunta es si el uso varchar(255)es más eficiente o ahorra más espacio en comparación con el uso varchar(5000). ¿Si es así por qué?

Tintín
fuente
¿Necesita una columna de 5000 caracteres de ancho? Si es así ¿POR QUÉ? ¿Una columna varchar (MAX) funcionaría mejor para usted aquí?
Richard L. Dawson

Respuestas:

52

Sí, varchar(5000)puede ser peor que varchar(255)si todos los valores encajaran en este último. La razón es que SQL Server estimará el tamaño de los datos y, a su vez, las concesiones de memoria en función del tamaño declarado (no real ) de las columnas en una tabla. Cuando lo haya hecho varchar(5000), supondrá que cada valor tiene una longitud de 2.500 caracteres y reservará memoria en función de eso.

Aquí hay una demostración de mi reciente presentación de GroupBy sobre malos hábitos que hace que sea fácil probarlo por sí mismo (requiere SQL Server 2016 para algunas de las sys.dm_exec_query_statscolumnas de salida, pero aún debe ser demostrable con SET STATISTICS TIME ONu otras herramientas en versiones anteriores); muestra una memoria más grande y tiempos de ejecución más largos para la misma consulta con los mismos datos ; la única diferencia es el tamaño declarado de las columnas:

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

Entonces, sí, dimensione correctamente sus columnas , por favor.

Además, volví a ejecutar las pruebas con varchar (32), varchar (255), varchar (5000), varchar (8000) y varchar (max). Resultados similares ( haga clic para ampliar ), aunque las diferencias entre 32 y 255, y entre 5,000 y 8,000, fueron insignificantes:

ingrese la descripción de la imagen aquí

Aquí hay otra prueba con el TOP (5000)cambio para la prueba más completamente reproducible por la que me acosaron sin cesar ( haga clic para agrandar ):

ingrese la descripción de la imagen aquí

Entonces, incluso con 5,000 filas en lugar de 10,000 filas (y hay más de 5,000 filas en sys.all_columns al menos tan atrás como SQL Server 2008 R2), se observa una progresión relativamente lineal, incluso con los mismos datos, cuanto mayor sea el tamaño definido de la columna, se necesita más memoria y tiempo para satisfacer exactamente la misma consulta (incluso si no tiene sentido DISTINCT).

Aaron Bertrand
fuente
Esto es realmente sorprendente. ¿La diferencia entre varchar(450)y varchar(255)sería la misma? (¿O algo por debajo de 4000?)
a_horse_with_no_name
@a_horse_with_no_name No he probado todas las permutaciones del rendimiento en tiempo de ejecución, pero la concesión de memoria sería una progresión lineal, es simplemente una función de rowcount*(column_size/2).
Aaron Bertrand
Eso es bastante decepcionante entonces. Pensé que las versiones modernas de SQL Server no sufren de eso (siempre que la longitud definida sea menor que 8000 o quizás 4000).
a_horse_with_no_name
1
@a_horse_with_no_name Bueno, tiene que adivinar qué tan anchos son los datos para evitar derrames. ¿De qué otra forma debería adivinar? No puede escanear y leer la tabla completa para determinar las longitudes promedio / máximo de todas las columnas de ancho variable como un precursor para generar un plan de ejecución (e incluso si pudiera, solo sería capaz de hacerlo durante una compilación).
Aaron Bertrand
2
Oracle mantiene estadísticas sobre, por ejemplo, la longitud promedio de fila, los valores mínimo y máximo para cada columna, así como un histograma. Postgres mantiene estadísticas muy similares (aunque no registra min / max, sino frecuencias). Para ninguno de ellos hay alguna diferencia entre nvarchar (150), nvarchar (2000) o varchar (400) en el rendimiento.
a_horse_with_no_name