Por que o MySQL permite que o HAVING use aliases SELECT?

14

No SQL, até onde eu sei, a ordem de processamento da consulta lógica, que é a ordem de interpretação conceitual, começa com FROM da seguinte maneira:

  1. A PARTIR DE
  2. ONDE
  3. GRUPO POR
  4. TENDO
  5. SELECT
  6. ORDENAR POR

Após esta lista, é fácil ver por que você não pode ter aliases SELECT em uma cláusula WHERE, porque o alias ainda não foi criado. O T-SQL (SQL Server) segue isso estritamente e você não pode usar aliases SELECT até que você tenha passado por SELECT.

Mas no MySQL é possível usar aliases SELECT na cláusula HAVING, mesmo que deva (logicamente) ser processada antes da cláusula SELECT. Como isso pode ser possível?

Para dar um exemplo:

SELECT YEAR(orderdate), COUNT(*) as Amount
FROM Sales.Orders
GROUP BY YEAR(orderdate) 
HAVING Amount>1;

A instrução é inválida no T-SQL (porque HAVING está se referindo ao alias SELECT Amount) ...

Msg 207, Level 16, State 1, Line 5
Invalid column name 'Amount'.

... mas funciona muito bem no MySQL.

Com base nisso, estou me perguntando:

  • O MySQL está usando um atalho nas regras SQL para ajudar o usuário? Talvez usando algum tipo de pré-análise?
  • Ou o MySQL está usando uma ordem de interpretação conceitual diferente daquela que eu todo o RDBMS estava seguindo?
Ohlin
fonte
1
Meu palpite é que é o seu segundo ponto.
A_horse_with_no_name
3
Bem, acho que isso não causa ambiguidade ou confusão até que eles suportem funções de classificação. Em seguida, SELECT C, ROW_NUMBER() OVER (ORDER BY X) AS RN FROM T GROUP BY C HAVING RN = 1será problemático como as ROW_NUMBERcorridas após oHAVING
Martin Smith
Não tenho certeza de quais funções de classificação são suportadas pelo MySQL. Se quiser que o número de linha que você tem que criá-lo desta forma: SELECT @rownum:=@rownum + 1 as row .... Talvez a razão pela qual eles apóiam os aliases SELECT simplesmente seja porque eles podem, devido ao fato de não apoiarem coisas que tornariam isso impossível ... quem sabe? :)
Ohlin
Como o @MartinSmith explica, desde que não haja funções de janela / classificação, a ordem lógica de execução HAVINGe a SELECTcláusula podem ser trocadas. Portanto, não há ambiguidade em fazer isso e pode simplificar a aparência do código quando houver expressões monstruosas SELECT.
usar o seguinte comando
Espero que isso seja um pouco sobre o tópico para dizer que eu respondi uma pergunta Aqui está desfrutando de resultados mais rápidos (com distincts) ... com a Alias in the Havingapesar da mesma Explainsaída. Então, alguma variação com o Optimizer está acontecendo.
Tirou

Respostas:

13

Bem, quando você tem uma pergunta desse tipo, a melhor fonte de informação IMHO é a documentação do MySQL. Agora ao ponto. Esse é o comportamento da extensão MySql ao GROUP BYqual está ativado por padrão.

Extensões do MySQL para GROUP BY O
MySQL estende esse comportamento para permitir o uso de um alias na cláusula HAVING para a coluna agregada

Se você deseja um comportamento padrão, pode desativar esta extensão com sql_mode ONLY_FULL_GROUP_BY

SET [SESSION | GLOBAL] sql_mode = ONLY_FULL_GROUP_BY;

Se você tentar executar a consulta acima mencionada em ONLY_FULL_GROUP_BYsql_mode, receberá a seguinte mensagem de erro:

O campo que não agrupa 'Valor' é usado na cláusula HAVING: SELECT YEAR (orderdate), COUNT (*) como o valor FROM Orders GROUP BY YEAR (datedate) HAVING Amount> 1

Aqui está a demonstração do SQLFiddle

Portanto, cabe a você como configurar e usar sua instância do MySQL.

peterm
fonte
Você está absolutamente certo sobre a documentação. Eu nunca pensei que pudesse ser tão claramente escrito como você citou acima :) Obrigado por encontrá-lo ...
Ohlin
Esta resposta não responde "O MySQL está fazendo uma pré-análise ou o MySQL está usando uma interpretação conceitual diferente?".
Pacerier
2
@Pacerier O MySQL está "fazendo pré-análise", é claro, porque o otimizador de consultas considera todas as facetas da consulta enquanto escolhe o que acredita ser o melhor plano de consulta. A noção de uma "interpretação conceitual diferente" revela um mal-entendido do fato de que o servidor é livre para implementar o modelo conceitual de qualquer maneira que produza um resultado válido. ORDER BY, por exemplo, pode ser realmente tratado muito mais cedo do que teoricamente, se o otimizador descobrir que as linhas podem ser lidas inicialmente na ordem de um índice que já está na ordem desejada.
Michael - sqlbot
4

Boa pergunta.

Eu acho que você deve executar essas consultas

EXPLAIN SELECT YEAR(orderdate), COUNT(*) as Amount
FROM Sales.Orders
GROUP BY YEAR(orderdate) 
HAVING Amount>1;
SHOW WARNINGS;

e verifique como a consulta é reescrita. Estamos certos de que o otimizador de consultas substitua Valor por COUNT (*)

SELECT YEAR(orderdate), COUNT(*) as Amount
FROM Sales.Orders
GROUP BY YEAR(orderdate) 
HAVING COUNT(*)>1;

Como acontece com

select 
 *
from 
 test
where 
 id = 5 - 3

após o otimizador de consultas, é algo assim.

select 
 test.id as 'id'
from 
 test
where 
 test.id = 2
Raymond Nijland
fonte