WITH ROLLUP
複数のフィールドでグループ化すると、MySQLは各グループのロールアップ行と全体的な概要を返します。
CREATE TABLE test (name VARCHAR(50), number TINYINT);
INSERT INTO test VALUES
('foo', 1), ('foo', 1), ('foo', 2), ('foo', 3), ('foo', 3),
('bar', 1), ('bar', 2), ('bar', 2), ('bar', 2), ('bar', 3),
('baz', 1), ('baz', 2), ('bar', 2);
SELECT name, number, COUNT(1) FROM test GROUP BY name, number WITH ROLLUP;
+------+--------+----------+
| name | number | count(1) |
+------+--------+----------+
| bar | 1 | 1 |
| bar | 2 | 3 |
| bar | 3 | 1 |
| bar | NULL | 5 |
| baz | 1 | 1 |
| baz | 2 | 2 |
| baz | NULL | 3 |
| foo | 1 | 2 |
| foo | 2 | 1 |
| foo | 3 | 2 |
| foo | NULL | 5 |
| NULL | NULL | 13 |
+------+--------+----------+
私はfoo/bar/bazのロールアップに飽き飽きしておらず、全体的な概要のみを読みました。これを達成するための最も効率的な方法は何ですか?
SELECT * FROM
(
SELECT name, number, COUNT(1) `count` FROM test
GROUP BY name, number WITH ROLLUP
) A WHERE (ISNULL(name) + ISNULL(number)) <> 1;
または
SELECT * FROM
(
SELECT name, number, COUNT(1) `count` FROM test
GROUP BY name, number WITH ROLLUP
) A WHERE ISNULL(name) = ISNULL(number);
合計が必要な場合は、次のようなunion all
クエリを使用する方が効率的です。
select name, number, count(1)
from test
group by name, number
union all
select null, null, count(1)
from test;
多くの属性がある場合、WITH ROLLUP
は多くのサブセットを生成しますが、それらは外部選択で捨てるだけです。 MySQLオプティマイザがそれらをスキップできることに気付かないと思いますが、それは単なる推測です。
また、HAVINGを試すこともできます。
SELECT name, number, COUNT(1)
FROM test GROUP BY name, number
WITH ROLLUP
HAVING (number is not null or name is null);
または
HAVING (number is null) = (name is null)
この場合、全体的な要約はレコードの量(頻度)に等しくなります。
SELECT count(1) FROM test;
数字の全体的な要約に興味があるなら
SELECT SUM(numbers) FROM test;
確かに、全体的な概要だけに関心がある場合は、それを要求するクエリを記述するだけです。
SELECT null, null, count(*) FROM test
(ロールアップですべての列にグループ化された13行のテストテーブルを作成し、テーブルの行数を効果的にカウントする行だけに関心があると言ったので、それを行ってください。)
詳細行と全体的な概要のみに関心がある場合は、それがグループ化セットの目的です。
SELECT name, number, count(*) FROM test GROUP BY GROUPING SETS ((name, number), ())
空のセットは要約を提供し、名前と番号のセットは詳細を提供します。 ROLLUPは、このグループ化セットと同等です。
((name, number), (name), ())