plano de execução diferente ao executar declaração direta e de procedimento armazenado
-
05-07-2019 - |
Pergunta
Ao desenvolver uma nova consulta no trabalho eu escrevi e perfilado-lo no SQL Query Analyzer. A consulta estava realizando realmente bom, sem quaisquer varreduras de tabela, mas quando eu encapsulado dentro de um procedimento armazenado o desempenho foi horrível. Quando olhei para o plano de execução Eu podia ver que o SQL Server pegou um plano diferente que usou uma varredura da tabela em vez de uma busca de índice em TableB (eu fui forçado a ofuscar os nomes de tabelas e colunas um pouco, mas nenhum dos lógica de consulta mudou).
Aqui está a consulta ??p>
SELECT
DATEADD(dd, 0, DATEDIFF(dd, 0, TableA.Created)) AS Day,
DATEPART(hh, TableA.Created) AS [Hour],
SUM(TableB.Quantity) AS Quantity,
SUM(TableB.Amount) AS Amount
FROM
TableA
INNER JOIN TableB ON TableA.BID = TableB.ID
WHERE
(TableA.ShopId = @ShopId)
GROUP BY
DATEADD(dd, 0, DATEDIFF(dd, 0, TableA.Created)),
DATEPART(hh, TableA.Created)
ORDER BY
DATEPART(hh, TableA.Created)
Quando eu executar a consulta "raw" Eu recebo as seguintes estatísticas de rastreamento
Event Class Duration CPU Reads Writes SQL:StmtCompleted 75 41 7 0
E quando eu executar a consulta como um proc armazenados usando o seguinte comando
DECLARE @ShopId int
SELECT @ShopId = 1
EXEC spStats_GetSalesStatsByHour @ShopId
Eu recebo as seguintes estatísticas de rastreamento
Event Class Duration CPU Reads Writes SQL:StmtCompleted 222 10 48 0
Eu também obter o mesmo resultado se eu armazenar a consulta em um nvarchar e executá-lo usando sp_executesql assim (ele executa como o sproc)
DECLARE @SQL nvarchar(2000)
SET @SQL = 'SELECT DATEADD(dd, ...'
exec sp_executesql @SQL
O procedimento armazenado não contém qualquer coisa, exceto para a instrução SELECT acima. O que faria com que o SQL Server para escolher um plano de execução inferior só porque a instrução é executada como um procedimento armazenado?
Nós estamos correndo atualmente no SQL Server 2000
Solução
Isso geralmente tem algo a ver com o parâmetro sniffing. Ele pode ser muito frustrante para lidar com eles. Às vezes pode ser resolvido por recompilar o procedimento armazenado, e às vezes você pode até usar uma variável duplicado dentro do procedimento armazenado como esta:
alter procedure p_myproc (@p1 int) as
declare @p1_copy int;
set @p1_copy = @p1;
E, em seguida, usar @ p1_copy na consulta. Parece ridículo, mas ele funciona.
Verifique minha pergunta recente sobre o mesmo tema:
Por que o SqlServer otimizador ficar tão confundido com parâmetros?
Outras dicas
Sim - Eu já tinha visto isso em banco de dados Oracle 11g bem - mesma consulta correu rápido em 2 nós de servidor db no SQL pronto, mas quando chamado de pacote que literalmente desligou
teve que limpar a piscina comum para obter um comportamento idêntico:. Motivo algum trabalho / script estava correndo que tinha cópia mais antiga trancada na biblioteca cache / memória em um nó com plano de execução inferior