Estou no processo de reescrever consultas que não extraem mais todos os dados necessários. Minha pergunta é em relação a uma prática que nunca vi e não encontrei nenhuma pergunta no StackExchange que aborde especificamente o problema.
Eu sei que o objetivo da HAVING
declaração é introduzir condições em agregações, assim como WHERE
introduz condições em linhas individuais. No entanto, o que estou vendo neste código está HAVING
sendo usado em vez de WHERE
consultas com agregações. As condições em HAVING
não são aplicadas nas agregações, mas nas colunas não agregadas.
Por exemplo:
SELECT id, filedate, SUM(amount)
FROM Sales
GROUP BY id, filedate
HAVING id = 123 AND filedate = '1/1/2018'
Ao contrário de:
SELECT id, filedate, SUM(amount)
FROM Sales
WHERE id = 123 AND filedate = '1/1/2018'
GROUP BY id, filedate
Existem implicações de desempenho ou outras vantagens/desvantagens para esta estratégia?
Eu não tentei executar diagnósticos por conta própria, não é uma prioridade e teria que fazer isso no meu próprio tempo. No entanto, acho que posso, se não houver uma resposta clara sobre isso.
Minha preocupação é como o otimizador visualiza essa consulta. Ele agrega todos os dados e, em seguida, restringe o conjunto de resultados com base na HAVING
cláusula ou percebe que pode aplicar as condições de ter nas linhas individuais, pois elas estão referenciando especificamente colunas não agregadas?
EDIT: Para minhas consultas de exemplo e o SQL real que estou reescrevendo, os planos são idênticos, mas as consultas são de complexidade semelhante e ainda não tenho conhecimento suficiente para tirar conclusões dos planos idênticos.
O problema aqui está em como você está descrevendo a que a
HAVING
cláusula se aplica. AHAVING
cláusula sempre se aplica a campos agregados , que são todas as colunas restantes após a agregação. Você está tentando mostrar/dizer que aHAVING
cláusula não está sendo aplicada a nenhuma função agregada , que é o que eles geralmente aplicam. Mas, na realidade, aHAVING
cláusula rege o resultado dessa função agregada ou, em seu primeiro exemplo, o resultado da coluna de agrupamento. Mas em ambos os casos, a agregação já foi realizada.Portanto, em termos de desempenho (sem mencionar a legibilidade para outras pessoas tentando atualizar este código posteriormente), você usa a
WHERE
cláusula para filtrar o que será agregado e, em seguida, aHAVING
cláusula para filtrar o que foiagregado. E, embora o resultado de um teste simples, como mostrado na pergunta, mascare a diferença entre o tempo dos dois (ou o posicionamento lógico na sequência em que a consulta é processada) de modo que "parecem" estar fazendo a mesma coisa, Eu ficaria bastante surpreso se não fosse menos eficiente agregar um monte de linhas apenas para jogá-las fora mais tarde, quando logicamente elas poderiam ter sido eliminadas antes de armazenar/computar as agregações. NO ENTANTO, se você ver que os planos de execução são semelhantes para este exemplo simples, estou disposto a apostar que é apenas devido ao otimizador ver que seria mais eficiente tornar essasHAVING
condições reaisWHERE
condições à medida que reescreve a consulta antes de executá-la. Mas, nesse caso, eu ainda desaconselharia escrever consultas dessa maneira porque você está fazendo com que o otimizador demore mais tempo para reescrever código ruim quando deveria estar gastando esse tempo / ciclos de CPU encontrando um plano mais eficiente. @DavidSpillett acrescentou (em um comentário sobre esta resposta): "Além disso, você está confiando no planejador de consultas vendo o potencial de otimização, que pode não ser em consultas mais complexas ou se seu código acabar sendo portado para outro banco de dados (ou mesmo apenas um versão mais antiga do SQL Server)".Por que vale a pena, até mesmo a documentação da Microsoft para a cláusula HAVING afirmava que ela agia como uma
WHERE
cláusula quando nãoGROUP BY
estava presente. Agora que a documentação está no GitHub, consegui corrigi-la recentemente via Pull Request #235: Corrigir e melhorar a cláusula HAVING .Solomon dá explicações muito boas, mas para mim, a resposta fácil é lembrar a ordem de processamento lógico da consulta SQL como Itzik Ben-Gan escreveu aqui A sequência é sempre
FROM -> WHERE -> GROUP BY -> HAVING -> SELECT -> ORDER BY
Então veja, se pudermos ter um filtro WHERE aplicado antes de GROUP BY, podemos reduzir a quantidade de dados a serem processados por GROUP BY, esp, a operação WHERE pode ser extremamente eficiente quando existem índices adequados. Como tal, eu diria que se usar WHERE e HAVING retornam o mesmo resultado da perspectiva do negócio, WHERE é sempre um vencedor sobre HAVING.