Каков правильный результат для этого запроса?

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 не компилируется ни с FRU DUAL, ни без него .
Андрей М
1
@AndriyM По какой-то неизвестной причине «dual» и «HAVING» плохо работают в MySQL. (Хорошая находка). Но эквивалент работает: SELECT 1 AS t FROM (SELECT 1) tmp WHERE 1=0 HAVING 1=1; 1-row-no-dual и возвращает 0 строк.
ypercubeᵀᴹ
1
@SQLKiwi - А как насчет этого отрывка из спецификации? Msgstr "Если TE не содержит сразу a <group by clause>, то “GROUP BY ()”подразумевается." Разве оба запроса не должны давать одинаковые результаты?
Мартин Смит
1
Но с этим не согласен (Oracle выполняет запросы по- HAVINGразному): SQl-fiddle 2: 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не содержит сразу <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 (определение GROUP BY CLAUSE)

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, похоже, противоречит сам себе, так как SELECT 1 FROM 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.

ypercubeᵀᴹ
источник
Итак, ключевой вопрос заключается в том, действительно ли это правда, что «даже если в таблице вообще нет строк, это все равно одна группа из 0 строк». И стандарт оказывается явным по этому поводу: «Если нет группирующих столбцов, тогда ... это сгруппированная таблица, состоящая из T как единственной группы». (и это справедливо даже в том случае, если T пусто - значит, действительно, есть группа.) Далее, в предложении have указано, что условие применяется к каждой группе (в примере, таким образом, один раз). Вероятно, они определили это так, чтобы SUM и COUNT возвращали одну строку даже для пустых T.
Эрвин Смут
+1 (раньше!) Несмотря на то, что ваша логика такая же, как у Кевина, я принял его ответ из-за цитат из спецификации. Благодарность!
Мартин Смит
@MartinSmith. Thnx. Что я получаю от того, чтобы быть ленивым :)
ypercubeᵀᴹ
@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 кажется мне более логичным и правильным ...

a1ex07
источник
1
Вы правы, что SQL Server не смотрит на таблицу. План выполнения просто имеет постоянное сканирование и даже не ссылается на таблицу. Если бы это был только SQL Server, я бы просто объяснил это ошибкой, но поскольку это не просто SQL Server, мне интересно, есть ли здесь какая-то подлинная двусмысленность.
Мартин Смит
PostgreSQL показывает те же результаты, что и SQLServer, и, насколько я могу судить по выводу explain«Result (lines = 1) ...» для наличия и «Seq Scan» для «WHERE», он также не смотрит в таблицу. .. Я думаю, это как-то связано с тем, что "FROM" не является обязательным в TSQL и PostgreSQL. Я знаю, что Mysql также не требует этого, но, поскольку они поддерживают dual, они, вероятно, анализируют запрос немного по-другому. Я согласен, это звучит как спекуляция, но я надеюсь, что это имеет смысл.
a1ex07