Почему MySQL позволяет HAVING использовать псевдонимы SELECT?


14

Насколько мне известно, в SQL логический порядок обработки запросов, который является концептуальным порядком интерпретации, начинается с FROM следующим образом:

  1. ИЗ
  2. ГДЕ
  3. ГРУППА ПО
  4. HAVING
  5. ВЫБРАТЬ
  6. СОРТИРОВАТЬ ПО

Из этого списка легко понять, почему в предложении WHERE нельзя использовать псевдонимы SELECT, поскольку псевдоним еще не создан. T-SQL (SQL Server) строго следует этому, и вы не можете использовать псевдонимы SELECT, пока не пройдете SELECT.

Но в MySQL можно использовать псевдонимы SELECT в предложении HAVING, даже если он должен (логически) обрабатываться до предложения SELECT. Как это может быть возможным?

Чтобы привести пример:

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

Оператор недопустим в T-SQL (потому что HAVING ссылается на псевдоним SELECT Amount) ...

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

... но отлично работает в MySQL.

Исходя из этого, мне интересно:

  • MySQL использует ярлык в правилах SQL, чтобы помочь пользователю? Может быть, с помощью какого-то предварительного анализа?
  • Или MySQL использует иной концептуальный порядок интерпретации, чем тот, который я следовал всем СУБД?

1
Я думаю, это ваша вторая точка пули.
a_horse_with_no_name

3
Ну, я думаю, это не вызывает никакой двусмысленности или путаницы, пока они не поддерживают функции ранжирования. Тогда SELECT C, ROW_NUMBER() OVER (ORDER BY X) AS RN FROM T GROUP BY C HAVING RN = 1будет проблематично , как ROW_NUMBERбежит послеHAVING
Martin Smith

Я не уверен, какие функции ранжирования поддерживаются MySQL. Если вы хотите номер строки , вы должны создать его таким образом: SELECT @rownum:=@rownum + 1 as row .... Возможно, причина, по которой они поддерживают псевдонимы SELECT, заключается просто в том, что они могут, из-за того, что они не поддерживают вещи, которые делают невозможным ... кто знает? :)
Олин

Как объясняет @MartinSmith, до тех пор, пока нет оконных / ранжирующих функций, логический порядок выполнения для HAVINGи SELECTпредложения можно поменять местами. Таким образом, в этом нет никакой двусмысленности, и он может упростить внешний вид кода при наличии чудовищных выражений SELECT.
ypercubeᵀᴹ

Надеюсь, это несколько по теме, чтобы сказать, что я ответил на вопрос Здесь, который имеет более быстрые результаты (с distincts) ... Alias in the Havingнесмотря на тот же Explainрезультат. Так что некоторые изменения с оптимизатором происходят.
Дрю

Ответы:


13

Хорошо, когда у вас есть вопрос такого рода, лучшим источником информации IMHO является документация MySQL. Теперь к делу. Это поведение расширения MySql, GROUP BYкоторое включено по умолчанию.

Расширения MySQL для GROUP BY
MySQL расширяет это поведение, чтобы разрешить использование псевдонима в предложении HAVING для агрегированного столбца

Если вы хотите стандартное поведение, вы можете отключить это расширение с помощью sql_mode ONLY_FULL_GROUP_BY

SET [SESSION | GLOBAL] sql_mode = ONLY_FULL_GROUP_BY;

Если вы попытаетесь выполнить вышеупомянутый запрос в ONLY_FULL_GROUP_BYsql_mode, вы получите следующее сообщение об ошибке:

Не группирующее поле «Сумма» используется в предложении HAVING: SELECT YEAR (orderdate), COUNT (*) в качестве суммы FROM Orders GROUP BY YEAR (orderdate) HAVING Amount> 1

Вот демо SQLFiddle

Поэтому вам решать, как настроить и использовать свой экземпляр MySQL.


Вы абсолютно правы насчет документации. Я просто никогда не думал, что это может быть так ясно написано, как ты цитировал это выше :) Спасибо, что нашел это ...
Олин

Этот ответ не отвечает «MySQL выполняет предварительный анализ или MySQL использует другую концептуальную интерпретацию?».
Pacerier

2
@Pacerier MySQL, конечно, «проводит предварительный анализ», потому что оптимизатор запросов учитывает все аспекты запроса, выбирая, как он считает, лучший план запроса. Понятие «другой концептуальной интерпретации» выдает неверное понимание того факта, что сервер свободен для реализации концептуальной модели любым способом, который дает действительный результат. ORDER BYНапример, он может быть обработан гораздо раньше, чем теоретически, если оптимизатор обнаружит, что строки могут быть изначально считаны в порядке, соответствующем индексу, который уже находится в желаемом порядке.
Майкл - sqlbot

4

Хороший вопрос.

Я думаю, что вы должны запустить эти запросы

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

и проверьте, как запрос переписан. я уверен, что оптимизатор запросов заменит Amount на COUNT (*)

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

Как это происходит с

select 
 *
from 
 test
where 
 id = 5 - 3

после оптимизатора запросов это примерно так.

select 
 test.id as 'id'
from 
 test
where 
 test.id = 2
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.