L'uso di varchar (5000) sarebbe negativo rispetto a varchar (255)?


28

Poiché varcharcomunque allocare lo spazio in modo dinamico, la mia domanda è se l'utilizzo varchar(255)sia più efficiente o risparmi più spazio rispetto all'utilizzo varchar(5000). Se si, perché?


Hai bisogno di una colonna larga 5000 caratteri? Se è così PERCHÉ? Una colonna varchar (MAX) funzionerebbe meglio qui?
Richard L. Dawson,

Risposte:


52

Sì, varchar(5000)può essere peggio che varchar(255)se tutti i valori si adatteranno a quest'ultimo. Il motivo è che SQL Server stimerà la dimensione dei dati e, a sua volta, le concessioni di memoria basate sulla dimensione dichiarata (non effettiva ) delle colonne in una tabella. In varchar(5000)tal caso, supporrà che ogni valore sia lungo 2.500 caratteri e riserverà la memoria in base a quello.

Ecco una demo della mia recente presentazione di GroupBy su cattive abitudini che rende facile dimostrarlo (richiede SQL Server 2016 per alcune delle sys.dm_exec_query_statscolonne di output, ma dovrebbe comunque essere dimostrabile con SET STATISTICS TIME ONo altri strumenti su versioni precedenti); mostra una memoria più grande e tempi di esecuzione più lunghi per la stessa query rispetto agli stessi dati - l'unica differenza è la dimensione dichiarata delle colonne:

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

Quindi, sì, ridimensiona le colonne , per favore.

Inoltre, ho eseguito nuovamente i test con varchar (32), varchar (255), varchar (5000), varchar (8000) e varchar (max). Risultati simili ( clicca per ingrandire ), sebbene le differenze tra 32 e 255 e tra 5.000 e 8.000, fossero trascurabili:

inserisci qui la descrizione dell'immagine

Ecco un altro test con la TOP (5000)modifica per il test più completamente riproducibile di cui ero incessantemente preoccupato ( clicca per ingrandire ):

inserisci qui la descrizione dell'immagine

Quindi, anche con 5.000 righe anziché 10.000 righe (e ci sono oltre 5.000 righe in sys.all_columns almeno fino a SQL Server 2008 R2), si osserva una progressione relativamente lineare - anche con gli stessi dati, maggiore è la dimensione definita della colonna, più memoria e tempo sono necessari per soddisfare esattamente la stessa query (anche se ha un significato DISTINCT).


questo è davvero sorprendente. La differenza tra varchar(450)e varchar(255)sarebbe la stessa? (O qualcosa al di sotto di 4000?)
a_horse_with_no_name

@a_horse_with_no_name Non ho testato tutte le permutazioni delle prestazioni di runtime, ma la concessione di memoria sarebbe una progressione lineare - è semplicemente una funzione di rowcount*(column_size/2).
Aaron Bertrand

È piuttosto deludente allora. Ho pensato che le versioni moderne di SQL Server non ne soffrano (a condizione che la lunghezza definita sia inferiore a 8000 o forse 4000).
a_horse_with_no_name

1
@a_horse_with_no_name Bene, deve fare un'ipotesi sull'ampiezza dei dati in modo da evitare fuoriuscite. In quale altro modo dovrebbe indovinare? Non può scansionare e leggere l'intera tabella per determinare le lunghezze avg / max di tutte le colonne a larghezza variabile come precursore per la generazione di un piano di esecuzione (e anche se potesse, sarebbe in grado di farlo solo durante una ricompilazione).
Aaron Bertrand

2
Oracle mantiene statistiche su, ad esempio, la lunghezza media della riga, i valori minimo e massimo per ciascuna colonna e un istogramma. Postgres mantiene statistiche molto simili (ma non registra min / max ma frequenze). Per nessuno dei due c'è alcuna differenza tra nvarchar (150), nvarchar (2000) o varchar (400) nelle prestazioni.
a_horse_with_no_name
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.