As funções de janela causam um plano de execução terrível quando chamado de uma exibição com cláusula 'where' parametrizada externa

10

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 whereclá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 scanem todas as tabelas ignorando índices. Retorna 5m de linhas.
  • Enorme junção.
  • Cálculo de janelas sobre todos os partitions (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?

GSerg
fonte
order_number é a chave primária? Tipos de dados de correspondência de coluna e parâmetro?
gbn 8/02/12
order_numbernão é uma chave primária. É int not nullcom índice não clusterizado nas duas tabelas.
GSerg 08/02/12
5
O SQL Server 2005 teve problemas com o envio de predicados nessa área. Eu pensei que eles estavam consertados agora. BTW, seu exemplo TSQL usa uma variável e não um parâmetro. A adição de OPTION (RECOMPILE)ajuda?
Martin Smith
11
@GSerg - Então, no plano ruim com o filtro, ele tem cerca de 5 milhões de linhas no filtro e 10 linhas correspondentes ao real? Se assim for, talvez seja esse problema de envio de predicado ainda não resolvido completamente.
Martin Smith

Respostas:

5

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 à recompiledica seria reescrever a consulta para usar um TVF embutido com parâmetros, conforme sugerido por @ a1ex07)

Martin Smith
fonte
Só tinha o caso no SQL Server 2014 como bem
Guillaume86
3

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_numberpara ele

a1ex07
fonte
Mais uma solução alternativa, sim. Não pude fazer isso porque nem todos os clientes foram capazes de consumir uma função com valor de tabela.
GSerg 08/02/12
Por quê? Eu não estou 100% de certeza, mas acho que tudo que você precisa é mudar consulta um pouco para algo comoSELECT * FROM my_funct(12345)
a1ex07
Um dos requisitos era que a consulta fosse consumível pelos usuários finais usando o Excel (ou seja, pelo MS Query), e o MS Query não permitirá que você faça isso, pelo menos nas versões até 2003.
GSerg
it will filter records first, and then apply window functionestá incorreto. Não há uma ordem determinística para a execução
Remus Rusanu