Який правильний результат для цього запиту?


20

Я натрапив на цю загадку в коментарях тут

CREATE TABLE r (b INT);

SELECT 1 FROM r HAVING 1=1;

Повернення SQL Server і PostgreSQL 1 рядок.

MySQL і Oracle повертають нульові рядки.

Що правильно? Або обидва однаково справедливі?


Гарна головоломка. Я думаю, що правильно - повернути 1 ряд. SQL-сервер суперечить собі, хоча SELECT COUNT(*) FROM r;повертає 1 рядок (з 0), а SELECT COUNT(*) FROM r GROUP BY ();повертає жодних рядків.
ypercubeᵀᴹ

1
Хочу більше? SELECT 1 WHERE 1=0 HAVING 1=1;. SQL Server і PostgreSQL все ще повертають один рядок. Oracle хоче від DUAL і не повертає жодних рядків. MySQL не компілюється ні з DUAL, ні без нього .
Андрій М

1
@AndriyM З незрозумілої причини "подвійний" та "HAVING" не грають добре в MySQL. (Приємна знахідка). Але еквівалент працює: SELECT 1 AS t FROM (SELECT 1) tmp WHERE 1=0 HAVING 1=1; 1-рядок-не-подвійний і повертає 0 рядків.
ypercubeᵀᴹ

1
@SQLKiwi - Що з цього уривку з специфікації. "Якщо TE не містить одразу <group by clause>, “GROUP BY ()”це неявно." Чи не повинні обидва запити повертати однакові результати?
Мартін Сміт

1
Але з цим не HAVING
погоджуйтеся

Відповіді:


17

Відповідно до стандарту:

SELECT 1 FROM r HAVING 1=1

засоби

SELECT 1 FROM r GROUP BY () HAVING 1=1

Цитування ISO / IEC 9075-2: 2011 7.10 Правило 1 синтаксису (частина визначення пункту HAVING):

Нехай HCбуде <having clause>. Нехай TEбуде те, <table expression>що відразу містить HC. Якщо TEне одразу містить a <group by clause>, то " GROUP BY ()" є неявним. Нехай Tбуде дескриптор таблиці, визначений <group by clause> GBCодразу, що міститься в, TEі нехай Rбуде результатом GBC.

Гаразд, що багато чого досить зрозуміло.


Затвердження: 1=1справжня умова пошуку. Я не буду наводити жодної цитати на це.


Тепер

SELECT 1 FROM r GROUP BY () HAVING 1=1

еквівалентно

SELECT 1 FROM r GROUP BY ()

Цитування ISO / IEC 9075-2: 2011 7.10 Загальне правило 1:

<search condition>Оцінюється для кожної групи R. Результатом <having clause>є згрупована таблиця тих груп R, для яких результат значення <search condition>True є.

Логіка: Оскільки умова пошуку завжди відповідає дійсності, результат є R, який є результатом групи за виразом.


Далі - уривок із Загальних правил 7.9 (визначення групи ГРУПИ ПО КЛАЗУ)

1) Якщо ні <where clause>вказується, то нехай Tбуде результат попереднього <from clause>; інакше нехай Tбуде результатом попереднього <where clause>.

2) Справа:

a) Якщо немає групувальних стовпців, то результатом <group by clause>є згрупована таблиця, що складається Tяк її єдина група.

Таким чином, ми можемо зробити висновок про це

FROM r GROUP BY ()

приводить до згрупованої таблиці, що складається з однієї групи, з нульовими рядками (оскільки R порожній).


Витяг із Загальних правил 7.12, який визначає Специфікацію запиту (він же SELECT):

1) Справа:

а) Якщо Tце не згрупована таблиця, то [...]

б) Якщо Tце згрупована таблиця, то

Справа:

i) Якщо Tмає 0 (нульових) груп, то нехай TEMP - порожня таблиця.

б) Якщо Tмає одну або кілька груп, то кожен <value expression>застосовується до кожної групи Tотримує таблицю TEMPз Mрядків, де Mця кількість груп в T. У i-му стовпці TEMP містяться значення, отримані оцінкою i-го <value expression>. [...]

2) Справа:

а) Якщо значення <set quantifier> DISTINCTне вказано, то результат <query specification>є TEMP.

Тому, оскільки таблиця має одну групу, вона повинна мати один рядок результатів.

Таким чином

SELECT 1 FROM r HAVING 1=1

повинен повернути набір результатів у 1 рядок.

QED


+1 Дякуємо, що пішли на всю цю неприємність! Як говорить @ypercube, SQL Server, здається, суперечить собі тут як ВИБІР 1 З r GROUP BY (); повертає нульові ряди, але процитований вами уривок здається цілком зрозумілим.
Мартін Сміт

Чи можу я запитати, де ти знайшов стандарт? Якщо ви скажете "на моїй книжковій полиці", я розчаруюся :)
dezso

Технічно я використав заключний проект міжнародного стандарту, а не сам стандарт. Згідно правил ISO / IEC допускаються лише редакційні (нетехнічні) зміни між FDIS та кінцевим стандартом. Стандарт розплітається на кілька частин. Частина 1 , частина 2 , частина 4 ...
Кевін Кеткарт

Частина 11 і Частина 14 . Частини 3,9,10 та 13 не були оновлені у 2011 році, і тому їх попередні версії застосовуються. Немає частини 12. Так само немає частин 5-8. Дивіться сторінку Вікіпедії для Sql: 2011 або самої частини 1 для пояснення того, що містить кожна частина.
Кевін Кеткарт

7

Якщо є HAVINGпункт, без WHEREпункту:

SELECT 1 FROM r HAVING 1=1;

... то GROUP BY ()є неявним. Отже, запит повинен бути еквівалентний:

SELECT 1 FROM r GROUP BY () HAVING 1=1;

... який повинен об'єднати всі рядки таблиці в одну групу (навіть якщо у таблиці взагалі немає рядків - це все одно одна група з 0 рядків) і повернути 1 рядок. Після HAVINGцього Trueстан не повинен мати жодного ефекту після цього.


З іншого боку, скільки рядків повинен мати такий запит, як цей повернення?

SELECT COUNT(*), MAX(b) FROM r;

Один, нуль або "нуль чи один", залежно від того, чи таблиця порожня чи ні "?

Я думаю, що один ряд, незалежно від того, скільки рядків rмає.


Ну ключовим питанням є те, чи справді це правда, що "навіть якщо таблиця взагалі не має рядків, це все одно одна група з 0 рядків". І стандарт виявляється з цього приводу явним: "Якщо немає групових стовпців, то ... - це згрупована таблиця, що складається з Т як єдиної групи". (і це справедливо, навіть якщо T порожній - значить, дійсно є група.) Далі, клавіша, яка має, визначає, що умова застосовується до кожної групи (у прикладі таким чином один раз). Вони, ймовірно, визначили це таким чином, щоб змусити SUM і COUNT повертати один рядок навіть для порожніх T.
Ервін Смоут

+1 (раніше!) Незважаючи на те, що ваша логіка така ж, як і Кевіна, я прийняв його відповідь через цитати з специфікації. Спасибі!
Мартін Сміт

@MartinSmith. Thnx. Це я отримую від того, що
лінуюся

@ypercube: +1 теж від мене. Я вирішив витратити додатковий час, щоб витягнути зі спекуляції, щоб довести, що десь не було прихованих слів, які би зробили вашу відповідь неправильною. Але як тільки я це зробив, я міг би також опублікувати це як повну відповідь. Так я і зробив.
Кевін Кеткарт

3
@ErwinSmout: Звичайно, ні. Однак це підлягає справедливому використанню законодавства про авторські права США. Відносно невеликі частини, які цитуються в контексті аналізу (тобто критики) твору, для освітніх цілей, що має незначний вплив на здатність твору до продажу.
Кевін Кеткарт

3

З того, що я бачу, схоже на те, що SQLServer і PostgerSQL взагалі не переймаються заглядом у таблицю:

CREATE TABLE r (b INT);
insert into r(b) values (1);
insert into r(b) values (2);
SELECT 1 FROM r HAVING 1=1;

також повертає лише один ряд. Навіть незважаючи на те, що це каже в документах SQLServer

Коли GROUP BY не використовується, HAVING поводиться як пункт WHERE.

це не вірно в цьому випадку - WHERE 1=1замість HAVINGповертає належну кількість рядків. Я б сказав, що це помилка оптимізатора (або принаймні помилка документації) ... План SQLServer показує "Постійне сканування" у випадку HAVINGта "сканування таблиці" для WHERE...

Поведінка Oracle і Mysql здається мені більш логічною і правильною ...


1
Ви праві, що SQL Server не дивиться на таблицю. План виконання просто має постійне сканування і навіть не посилається на таблицю. Якби це був тільки SQL Server, я б просто поставив його на помилку, але оскільки це не лише SQL Server, мені цікаво, чи є тут справжня неоднозначність.
Мартін Сміт

PostgreSQL показує ті самі результати, що і SQLServer, і наскільки я можу сказати з виводу explain"Результат (рядки = 1) ..." для наявності та "Seq Scan" для "WHERE", він також не заглядає в таблицю. .. Я думаю, це якось пов'язано з тим, що "FROM" не є обов'язковим у TSQL та PostgreSQL. Я знаю, що Mysql також цього не вимагає, але оскільки вони підтримують dual, вони, ймовірно, аналізують запит дещо іншим. Я погоджуюся, це звучить як спекуляція, але, сподіваюся, це має певний сенс.
a1ex07
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.