ここでは、_grouping sets
_を使用した2つの類似したクエリがあり、SELECT
句には、集計で計算されたいくつかの式が含まれています。
_SELECT RN10, RN10 / 10, COUNT(*) FROM
(
SELECT RN, RN/10 AS RN10, RN/100 AS RN100 FROM
(
SELECT RN = -1 + ROW_NUMBER() OVER (ORDER BY 1/0)
FROM master..spt_values
) A
) B
GROUP BY GROUPING SETS ((RN10), (RN10 / 10), ())
ORDER BY 1, 2
_
それの計画はここにあります: 最初のクエリ計画
そして
_SELECT RN10, SUBSTRING(RN,3,99), COUNT(*) FROM
(
SELECT RN, SUBSTRING(RN,2,99) AS RN10 FROM
(
SELECT RN = CAST(-1 + ROW_NUMBER() OVER (ORDER BY 1/0) AS VARCHAR(99))
FROM master..spt_values
) A
) B
GROUP BY GROUPING SETS ((RN10), (SUBSTRING(RN,3,99)), ())
ORDER BY 1, 2
_
対応するプランはここにあります: 2番目のクエリプラン
両方のクエリは、最初に何らかの集計式を計算します。最初の場合は_RN10 / 10
_で、2番目の場合はSUBSTRING(RN,3,99)
です。次に、同じ式がSELECT
句で使用されますが、最初のプランではそれが再最初のクエリで計算され、2番目のクエリではありません。
その結果、最初の結果セットには、予想外のNULL
sが含まれています。
最初のクエリが計算を2回(集計で1回、最後のselect
でもう1回)実行するのに対し、2番目のクエリでは1回だけ計算する理由を誰かが説明できますか?
期待される結果が何であるかが明確である、より簡単な例を使用します。
_CREATE TABLE Queen
(
FirstName VARCHAR(7),
Surname VARCHAR(7)
);
INSERT INTO Queen
(FirstName, Surname)
VALUES
('Brian', 'May'),
('Freddie', 'Mercury'),
('John', 'Deacon'),
('Roger', 'Taylor')
;
_
クエリ1
_SELECT Surname,
NULL AS SurnameInitial,
COUNT(*) AS Count
FROM Queen
GROUP BY Surname
UNION ALL
SELECT NULL AS Surname,
LEFT(Surname,1) AS SurnameInitial,
COUNT(*) AS Count
FROM Queen
GROUP BY LEFT(Surname,1)
_
クエリ1の結果
_+---------+----------------+-------+
| Surname | SurnameInitial | Count |
+---------+----------------+-------+
| Deacon | NULL | 1 |
| May | NULL | 1 |
| Mercury | NULL | 1 |
| Taylor | NULL | 1 |
| NULL | D | 1 |
| NULL | M | 2 |
| NULL | T | 1 |
+---------+----------------+-------+
_
クエリ2
_SELECT Surname,
LEFT(Surname,1) AS SurnameInitial,
COUNT(*) AS Count
FROM Queen
GROUP BY GROUPING SETS ( ( Surname ), (LEFT(Surname,1)) )
ORDER BY SurnameInitial, Surname
_
クエリ2の結果
_ORDER BY SurnameInitial
_とNULL
がSQL Serverで最初にソートされるという事実にもかかわらず、SurnameInitial
がNULL
である行は最後に並べられます。
_+---------+----------------+-------+
| Surname | SurnameInitial | Count |
+---------+----------------+-------+
| Deacon | D | 1 |
| May | M | 1 |
| Mercury | M | 1 |
| Taylor | T | 1 |
| NULL | NULL | 1 |
| NULL | NULL | 2 |
| NULL | NULL | 1 |
+---------+----------------+-------+
_
クエリ1および2 shouldは同じ結果を返します。問題は、SQL Serverが次のSQLのように扱うことを決定することです。
_WITH GrpSets AS
(
SELECT Surname,
COUNT(*) AS Count
FROM Queen
GROUP BY Surname
UNION ALL
SELECT NULL AS Surname,
COUNT(*) AS Count
FROM Queen
GROUP BY LEFT(Surname,1)
)
SELECT Surname,
LEFT(Surname,1) AS SurnameInitial,
Count
FROM GrpSets
_
これは私にとってバグのように見えます(トレースフラグ8605は、最初のクエリツリー表現で損傷が既に行われていることを示しています)。 バグレポート 。
クエリ3
_SELECT Surname,
LEFT(FirstName,1) AS FirstNameInitial,
COUNT(*) AS Count
FROM Queen
GROUP BY GROUPING SETS ( ( Surname ), (LEFT(FirstName,1)) )
_
クエリ3の結果
_+---------+------------------+-------+
| Surname | FirstNameInitial | Count |
+---------+------------------+-------+
| NULL | B | 1 |
| NULL | F | 1 |
| NULL | J | 1 |
| NULL | R | 1 |
| Deacon | NULL | 1 |
| May | NULL | 1 |
| Mercury | NULL | 1 |
| Taylor | NULL | 1 |
+---------+------------------+-------+
_
Query3は、列とその列を参照する式のグループ化の問題のあるパターンを満たしていません。とにかくここで同じ問題が発生することは不可能です
_SELECT Surname,
NULL AS FirstNameInitial,
COUNT(*) AS Count
FROM Queen
GROUP BY Surname
UNION ALL
SELECT NULL AS Surname,
LEFT(FirstName,1) AS FirstNameInitial,
COUNT(*) AS Count
FROM Queen
GROUP BY LEFT(FirstName,1)
_
これは、FirstName
列全体を上流に渡さない(またはcouldが渡されることが保証された一意のFirstName
列を持っている)ため、その上で計算されるLEFT(FirstName,1)
式。
同じ理由で、_(RN10), (SUBSTRING(RN,3,99))
_の問題は発生しません。
@ i-one おそらくコメントにある理由
正規化(代数化)のバグ。これには、_
GROUP BY
_のメンバー内のSELECT
リスト内の集計されていない列と式の一致を見つけるロジックがあります。同じロジックにより、たとえば次のように書くことができます_SELECT Surname, LEFT(Surname, 1), COUNT(*) FROM Queen GROUP BY Surname
_
以下のように計算式を明示的に追加する必要なし
_GROUP BY Surname, LEFT(Surname, 1)
_
または別の例は
_SELECT Surname,
LEFT(Surname,1) AS SurnameInitial,
LEFT(Surname,2) AS SurnamePrefix,
COUNT(*) AS Count
FROM Queen
GROUP BY GROUPING SETS ( ( Surname ), (LEFT(Surname,1)) )
_
この場合、LEFT(Surname,2)
が許可され、LEFT(Surname,1)
の場合に問題となる方法で計算することが唯一の計算方法になります。