Eu tive esse problema há muito tempo, encontrei uma solução alternativa que me convinha e esqueci.
Mas agora há essa pergunta no SO, então estou disposto a trazer esse problema à tona.
Há uma visão que une poucas tabelas de maneira muito direta (pedidos + linhas de pedidos).
Quando consultada sem uma where
cláusula, a exibição retorna vários milhões de linhas.
No entanto, ninguém nunca chama assim. A consulta usual é
select * from that_nasty_view where order_number = 123456;
Isso retorna cerca de 10 registros de 5m.
Uma coisa importante: a visualização contém uma função da janela rank()
, que é particionada exatamente pelo campo usando o qual a visualização é sempre consultada:
rank() over (partition by order_number order by detail_line_number)
Agora, se essa visualização for consultada com parâmetros literais na string de consulta, exatamente como mostrado acima, ela retornará as linhas instantaneamente. O plano de execução está bom:
- Procura de índice em ambas as tabelas usando os índices on
order_number
(retorna 10 linhas). - Cálculo de janelas sobre o pequeno resultado retornado.
- Selecionando.
No entanto, quando a exibição é chamada de maneira parametrizada, as coisas ficam desagradáveis:
Index scan
em todas as tabelas ignorando índices. Retorna 5m de linhas.- Enorme junção.
- Cálculo de janelas sobre todos os
partition
s (cerca de 500k janelas). Filter
tirar 10 linhas de 5m.- Selecione
Isso acontece em todos os casos quando parâmetros estão envolvidos. Pode ser SSMS:
declare @order_number int = 123456;
select * from that_nasty_view where order_number = @order_number;
Pode ser um cliente ODBC, como o Excel:
select * from that_nasty_view where order_number = ?
Ou pode ser qualquer outro cliente que use parâmetros e não concatenação sql.
Se a função da janela for removida da exibição, ela será executada perfeitamente rapidamente, independentemente de ser ou não consultada com parâmetros.
Minha solução alternativa foi remover a função incorreta e reaplicá-la posteriormente.
Mas o que dá? É realmente um bug na maneira como o SQL Server 2008 lida com as funções da janela?
order_number
não é uma chave primária. Éint not null
com índice não clusterizado nas duas tabelas.OPTION (RECOMPILE)
ajuda?Respostas:
Parece ser um problema de longa data que continua aparecendo de uma forma ou de outra e ainda está presente no SQL Server 2012.
Algumas postagens discutindo isso são
Todas as versões atuais do SQL Server até 2012, inclusive, não podem enviar por push o filtro em um grupo de particionamento além do projeto de sequência para um predicado parametrizado, exceto se
option(recompile)
for usado (se 2008 ou mais).Uma alternativa à
recompile
dica seria reescrever a consulta para usar um TVF embutido com parâmetros, conforme sugerido por @ a1ex07)fonte
Eu tentaria substituir a exibição por udf com valor de tabela. Dessa forma, ele primeiro filtra os registros e depois aplica a função de janela. Esta função pode aceitar parâmetro de tabela para que você possa passar vários
order_number
para elefonte
SELECT * FROM my_funct(12345)
it will filter records first, and then apply window function
está incorreto. Não há uma ordem determinística para a execução