Чому MySQL дозволяє HAVING використовувати псевдоніми SELECT?


14

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

  1. ВІД
  2. ДЕ
  3. ГРУПА ПО
  4. ВИДАЛЕНО
  5. ВИБІРИ
  6. СОРТУВАТИ ПО

Після цього списку легко зрозуміти, чому ви не можете мати псевдоніми SELECT у пункті WHERE, оскільки псевдонім ще не створений. 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 використовує інший концептуальний порядок інтерпретації, ніж той, який я дотримувався всім RDBMS?

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 (дата замовлення), COUNT (*) як сума від замовлень GROUP BY YEAR (дата замовлення) HAVING Сума> 1

Ось демонстрація SQLFiddle

Тому ви вирішуєте, як налаштувати та використовувати ваш примірник MySQL.


Ви абсолютно праві щодо документації. Я просто ніколи не думав, що це може бути так чітко написано, як ви цитували його вище :) Дякую, що знайшли ...
Олін,

Ця відповідь не відповідає "Чи MySQL робить попередній аналіз чи MySQL використовує іншу концептуальну інтерпретацію?".
Pacerier

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

4

Гарне питання.

Я думаю, вам слід запустити ці квери

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

і перевірте, як запит переписаний. я впевнений, що оптимізатор запитів замінить суму на 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.