このクエリの正しい結果は何ですか?


20

ここのコメントでこのパズルに出会いました

CREATE TABLE r (b INT);

SELECT 1 FROM r HAVING 1=1;

SQL ServerPostgreSQLは 1行を返します

MySQLおよびOracleはゼロ行を返します。

どちらが正しい?または、両方とも同等に有効ですか?


素敵なパズル。正しいのは1行を返すことだと思います。ただし、SQL-ServerはSELECT COUNT(*) FROM r;1行(を含む0)をSELECT COUNT(*) FROM r GROUP BY ();返し、行を返さないため、矛盾しています。
ypercubeᵀᴹ

1
もっと欲しい?SELECT 1 WHERE 1=0 HAVING 1=1;SQL ServerPostgreSQLは引き続き1行を返します。OracleはFROM DUALを必要とし、行を返しません。MySQLはFROM DUAL なしでもコンパイルせずにコンパイルしません。
アンドリーM

1
@AndriyM何らかの不明な理由により、「デュアル」と「ハビング」は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
しかし、これらに同意しない(Oracleが持つクエリを実行しHAVING、異なる):SQL-フィドル2:違う事をHAVING
ypercubeᵀᴹ

回答:


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>すぐに含まれていますHCTEがすぐに含まれない 場合<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であり、これはgroup by式の結果です。


以下は、7.9の一般規則(GROUP BY CLAUSEの定義)からの抜粋です。

1)no <where clause>が指定されている場合、let Tは前の結果になり<from clause>ます; それ以外の場合Tは、先行の結果とし<where clause>ます。

2)ケース:

a)グループ化列がない場合、の結果は、唯一のグループとして<group by clause>構成されるグループ化されたテーブルTです。

したがって、我々はそれを結論付けることができます

FROM r GROUP BY ()

1行のグループで構成されるグループ化されたテーブルになり、行はゼロになります(Rは空なので)。


クエリ仕様(別名SELECTステートメント)を定義する7.12の一般規則からの抜粋:

1)ケース:

a)Tグループ化されたテーブルでない場合、[...]

b)Tグループ化されたテーブルの場合、

場合:

i)Tグループが0(ゼロ)の場合、TEMPを空のテーブルにします。

ii)T1つ以上のグループがある場合<value expression>、各グループに適用され、行のTテーブルTEMPを生成します。MここでMはのグループ数ですTiTEMP の-th列には、i-thの評価によって導出された値が含まれます<value expression>。[...]

2)ケース:

a)<set quantifier> DISTINCTが指定されていない場合、の結果は<query specification>ですTEMP

したがって、テーブルには1つのグループがあるため、1つの結果行が必要です。

かくして

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;

...これは、テーブルのすべての行を1つのグループにグループ化する必要があり(テーブルに行がまったくない場合でも、0行の1つのグループです)、1行を返します。条件は、後に全く影響を与えないはずです。HAVINGTrue


別の角度から、このようなクエリは何行返す必要がありますか?

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

1、0、または「0または1、テーブルが空かどうかによって異なりますか?」

rがいくつあっても、1行だと思います。


重要な問題は、「テーブルに行がまったくない場合でも、0行の1つのグループである」という事実が本当かどうかです。そして、標準はこれについて明示的であることが判明します:「グループ化列がない場合、...はTを唯一のグループとして構成するグループ化されたテーブルです」。(そして、Tが空の場合でも成立します。したがって、実際にグループがあります。)さらに、having句は、条件が各グループに適用されることを指定します(この例では1回)。彼らはおそらく、空のTに対してもSUMとCOUNTが1行を返すようにこのように定義しました。
アーウィンスモート

+1(以前!)あなたの論理はKevinのものと同じですが、仕様からの引用のため、私は彼の答えを受け入れました。ありがとう!
マーティンスミス

@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;

また、1行のみを返します。にもかかわらず、SQLServerのドキュメントは言います

GROUP BYを使用しない場合、HAVINGはWHERE句のように動作します。

この場合はそうではありません- 適切な行数を返すWHERE 1=1代わりにHAVING。私はそれがオプティマイザーのバグ(または少なくともドキュメントのバグ)だと思うでしょう... SQLServerプランは、「Constant scan」の場合HAVINGと「table scan」を表示しWHEREます...

OracleとMysqlの動作は私にとってより論理的で正しいようです...


1
SQL Serverがテーブルを見ていないことは確かです。実行プランは常にスキャンを実行し、テーブルを参照することさえしません。SQL Serverのみの場合は、バグを報告するだけでしたが、SQL Serverだけではないので、ここに本当のあいまいさがあるのではないかと考えています。
マーティンスミス

PostgreSQLはSQLServerと同じ結果を示しますが、explain「Result(rows = 1)...」の出力から、「WHERE」の「Seq Scan」の出力からわかる限り、テーブルも調べません。 .. TSQLとPostgreSQLでは「FROM」が必須ではないという事実に何らかの関係があると思います。Mysqlもそれを必要としないことは知っていますが、サポートしているためdual、おそらく少し異なるクエリを解析します。憶測のように聞こえますが、私は同意します。
a1ex07
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.