Usar varchar (5000) seria ruim em comparação com varchar (255)?

28

De varcharqualquer maneira, aloque espaço dinamicamente, minha pergunta é se o uso deve varchar(255)ser mais eficiente ou economizar mais espaço em comparação ao uso varchar(5000). Se sim, por que?

Tintim
fonte
Você precisa de uma coluna com 5000 caracteres? Se sim, POR QUE? Uma coluna varchar (MAX) funcionaria melhor para você aqui?
Richard L. Dawson

Respostas:

52

Sim, varchar(5000)pode ser pior do que varchar(255)se todos os valores se ajustarem ao último. O motivo é que o SQL Server estimará o tamanho dos dados e, por sua vez, as concessões de memória com base no tamanho declarado (não real ) das colunas em uma tabela. Quando você tiver varchar(5000), ele assumirá que todo valor possui 2.500 caracteres e reserva memória com base nisso.

Aqui está uma demonstração da minha recente apresentação do GroupBy sobre maus hábitos que facilita a prova por si mesmo (requer o SQL Server 2016 para algumas das sys.dm_exec_query_statscolunas de saída, mas ainda deve ser comprovável com SET STATISTICS TIME ONou outras ferramentas em versões anteriores); ele mostra maior memória e mais longos tempos de execução para a mesma consulta contra os mesmos dados - a única diferença é o tamanho declarado das colunas:

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

Então, sim, dimensione corretamente suas colunas , por favor.

Além disso, refiz os testes com varchar (32), varchar (255), varchar (5000), varchar (8000) e varchar (max). Resultados semelhantes ( clique para ampliar ), embora as diferenças entre 32 e 255 e entre 5.000 e 8.000 fossem insignificantes:

insira a descrição da imagem aqui

Aqui está outro teste com a TOP (5000)alteração do teste mais totalmente reprodutível sobre o qual eu estava sendo incessantemente atormentado ( clique para ampliar ):

insira a descrição da imagem aqui

Portanto, mesmo com 5.000 linhas em vez de 10.000 linhas (e há mais de 5.000 linhas em sys.all_columns pelo menos no SQL Server 2008 R2), uma progressão relativamente linear é observada - mesmo com os mesmos dados, quanto maior o tamanho definido da coluna, mais memória e tempo são necessários para satisfazer exatamente a mesma consulta (mesmo que ela não tenha sentido DISTINCT).

Aaron Bertrand
fonte
isso é realmente surpreendente. A diferença entre varchar(450)e varchar(255)seria a mesma? (Ou qualquer coisa abaixo de 4000?)
a_horse_with_no_name
@a_horse_with_no_name Eu não testei todas as permutações do desempenho em tempo de execução, mas a concessão de memória seria uma progressão linear - é simplesmente uma função de rowcount*(column_size/2).
Aaron Bertrand
Isso é muito decepcionante então. Eu acho que as versões modernas do SQL Server não sofrem com isso (desde que o comprimento definido seja menor que 8000 ou talvez 4000).
a_horse_with_no_name
11
@a_horse_with_no_name Bem, ele precisa adivinhar a largura dos dados para evitar derramamentos. De que outra forma deveria adivinhar? Ele não pode varrer e ler a tabela inteira para determinar os comprimentos médios / máximos de todas as colunas de largura variável como precursor da geração de um plano de execução (e, mesmo que pudesse, só seria capaz de fazer isso durante uma recompilação).
Aaron Bertrand
2
O Oracle mantém estatísticas sobre, por exemplo, o comprimento médio da linha, os valores mínimo e máximo de cada coluna, bem como um histograma. O Postgres mantém estatísticas muito semelhantes (embora não registre min / max, mas frequências). Para nenhum deles existe qualquer diferença entre nvarchar (150), nvarchar (2000) ou varchar (400) no desempenho.
precisa saber é o seguinte