Em um servidor com 32 GB, estamos executando o SQL Server 2014 SP2 com uma memória máxima de 25 GB, temos duas tabelas, aqui você encontra uma estrutura simplificada de ambas as tabelas:
CREATE TABLE [dbo].[Settings](
[id] [int] IDENTITY(1,1) NOT NULL,
[resourceId] [int] NULL,
[typeID] [int] NULL,
[remark] [varchar](max) NULL,
CONSTRAINT [PK_Settings] PRIMARY KEY CLUSTERED ([id] ASC)
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[Resources](
[id] [int] IDENTITY(1,1) NOT NULL,
[resourceUID] [int] NULL,
CONSTRAINT [PK_Resources] PRIMARY KEY CLUSTERED ([id] ASC)
) ON [PRIMARY]
GO
com os seguintes índices não agrupados em cluster:
CREATE NONCLUSTERED INDEX [IX_UID] ON [dbo].[Resources]
(
[resourceUID] ASC
)
CREATE NONCLUSTERED INDEX [IX_Test] ON [dbo].[Settings]
(
[resourceId] ASC,
[typeID] ASC
)
O banco de dados está configurado com compatibility level
120.
Quando executo essa consulta, há derramamentos para tempdb
. É assim que executo a consulta:
exec sp_executesql N'
select r.id,remark
FROM Resources r
inner join Settings on resourceid=r.id
where resourceUID=@UID
ORDER BY typeID',
N'@UID int',
@UID=38
Se não selecionar o [remark]
campo, nenhum derramamento ocorrerá. Minha primeira reação foi que os derramamentos ocorreram devido ao baixo número de linhas estimadas no operador de loop aninhado.
Portanto, adiciono 5 colunas datetime e 5 inteiros à tabela de configurações e as adiciono à minha instrução select. Quando executo a consulta, não ocorrem derramamentos.
Por que os derramamentos acontecem apenas quando [remark]
é selecionado? Provavelmente tem algo a ver com o fato de que este é um varchar(max)
. O que posso fazer para evitar derramamento tempdb
?
Adicionar OPTION (RECOMPILE)
à consulta não faz diferença.
fonte
select r.id, LEFT(remark, 512)
(ou qualquer que seja o tamanho sensível da substring).Respostas:
Haverá várias soluções possíveis aqui.
Você pode ajustar manualmente a concessão de memória, embora eu provavelmente não siga esse caminho.
Você também pode usar um CTE e TOP para empurrar a classificação para baixo, antes de pegar a coluna de comprimento máximo. Será algo como abaixo.
Dbfiddle da prova de conceito aqui . Os dados de amostra ainda seriam apreciados!
Se você quiser ler uma excelente análise de Paul White, leia aqui.
fonte
O derramamento ocorre quando você inclui essa coluna, porque você não recebe uma concessão de memória grande o suficiente para os dados de string grandes que estão sendo classificados.
Você não recebe uma concessão de memória grande o suficiente porque o número real de linhas é 10 vezes maior que o número estimado de linhas (1.302 reais versus 126 estimadas).
Por que a estimativa está desativada? Por que o SQL Server acha que há apenas uma linha no dbo.Settings com um
resourceid
de 38?Pode ser um problema de estatística, que você pode verificar executando
DBCC SHOW_STATISTICS('dbo.Settings', 'IX_Test')
e ver as contagens dessa etapa do histograma. Mas o plano de execução parece indicar que as estatísticas são as mais completas e atualizadas possível.Como as estatísticas não estão ajudando, sua melhor aposta é provavelmente uma reescrita da consulta - que Forrest abordou em sua resposta.
fonte
Para mim, parece que a
where
cláusula na consulta está fornecendo o problema e é a causa das baixas estimativas, mesmo queOPTION(RECOMPILE)
sejam usadas.Criei alguns dados de teste e, no final, criei duas soluções, armazenando o
ID
camporesources
em uma variável (se sempre for única) ou em uma tabela temporária, se pudermos ter mais de umaID
.Registros de teste base
Insira os valores 'Procurar', para obter o mesmo conjunto de resultados aproximado que OP (1300 registros)
Altere as estatísticas compat & Update para corresponder ao OP
Consulta original
Minhas estimativas são ainda piores , com uma linha estimada, enquanto 1300 são retornadas. E, como o OP afirmou, não importa se eu adicionar
OPTION(RECOMPILE)
Uma coisa importante a ser observada é que, quando nos livramos da cláusula where, as estimativas estão 100% corretas, o que é esperado, pois estamos usando todos os dados nas duas tabelas.
Forcei os índices apenas para garantir que usamos os mesmos da consulta anterior, para provar o ponto
Como esperado, boas estimativas.
Então, o que poderíamos mudar para obter melhores estimativas, mas ainda buscar nossos valores?
Se @UID for único, como no exemplo que o OP deu, poderíamos colocar o único
id
retornadoresources
em uma variável e procurar nessa variável com um OPTION (RECOMPILE)O que fornece estimativas 100% precisas
Mas e se houver vários resourceUIDs em recursos?
adicione alguns dados de teste
Isso pode ser resolvido com uma tabela temporária
Novamente com estimativas precisas .
Isso foi feito com meu próprio conjunto de dados, YMMV.
Escrito com sp_executesql
Com uma variável
Com uma tabela temporária
Ainda estimativas 100% corretas no meu teste
fonte