※現象としては再現可能で回避方法もわかっていますが、なぜエラーになるのかは理解できていません。
MySQL5.6.4では発生しなかったため、MySQL5.7か8起因(5.7では未検証)かと考えられます。
実行環境
- Amazon RDS for MySQL(バージョン8.0.21)
- インスタンスタイプ:db.r6g.large
- ストレージ:汎用100GB
- クライアント:Sequel Nightly Build(5446)
データ準備
例えば次のようなテーブルを準備します。
CREATE TABLE `orders` (
`user_id` varchar(10) DEFAULT NULL,
`order_id` int DEFAULT NULL,
`date` date DEFAULT NULL,
`money` int DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `orders` (`user_id`, `order_id`, `date`, `money`)
VALUES
('AAA',1111,'2021-03-01',1000),
('AAA',2222,'2021-03-02',2000),
('AAA',3333,'2021-03-03',3000),
('BBB',4444,'2021-03-01',2000),
('BBB',5555,'2021-03-02',4000),
('BBB',6666,'2021-03-03',8000);
CREATE TABLE `order_detail` (
`order_id` int DEFAULT NULL,
`unit_price` int DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `order_detail` (`order_id`, `unit_price`)
VALUES
(1111,500),
(2222,1000),
(3333,3000),
(4444,2000),
(5555,2000),
(6666,8000),
(1111,500),
(2222,1000),
(5555,2000);
CREATE VIEW order_detail_view as select
order_id,
unit_price,
"不明" as item_category
FROM order_detail
;
エラーになるときの書き方
目的としては、似たようなデータ(例えばECの売上など)で、
こちらのデータにはAという項目があるけどこちらには無い、
というようなデータ間での差を構造として共通化することで、同じSQLで様々なデータの分析をできるようにするためです。
構造をあわせるにあたり、VIEWを使用すればテーブルをいじらなくて済むので良いかなと考え、
存在しない列をVIEW内でダミーとして作成しています。
ただ、次に示す通り、VIEWかどうかは関係が無いようです。
SELECT
item_category,COUNT(*)
FROM
orders a
LEFT JOIN
order_detail_view b
ON
a.order_id = b.order_id
GROUP BY
b.item_category;
#このようにVIEWにしなくてもエラーになる
SELECT
item_category,COUNT(*)
FROM
orders a
LEFT JOIN
(SELECT
*,"不明" AS item_category #ここが問題
FROM
order_detail) b
ON
a.order_id = b.order_id
GROUP BY
b.item_category;
>Duplicate entry '' for key '/rdsdbdata/tmp/*****.<group_key>'
#*****の部分にはなにかIDのようなものが入っていますが、何が載ってるか不明なのでマスクしています。
このような書き方をするとエラーが発生します。
ダミーとして作った列をGROUP BYで指定し、かつカウントを取ろうとしていました。
これがダミーでなければitem_categoryごとの件数を取得するものになります。
回避方法
1.文字列ではなくNULLや空文字にする
"不明"のような文字列ではなく""やNULLを指定すると集計できます。
2.ダミー列を先に内部でJOINする
このように一度括ってしまえばエラーになりません。
SELECT
item_category,COUNT(*)
FROM
orders a
LEFT JOIN
(SELECT
*
FROM
order_detail
JOIN
(SELECT "不明" AS item_category)q1 #この部分
) b
ON
a.order_id = b.order_id
GROUP BY
b.item_category;
3.LEFT JOINじゃなくINNER JOINにする
1のLEFT JOINをINNER JOINにすると動きます。
実行計画
実際に動かすとエラーにはなりますが、実行計画は見ることができます。
ここで見る限りおかしな点が見当たりません。
EXPLAIN
SELECT
item_category,COUNT(*)
FROM
orders a
LEFT JOIN
(SELECT
*,"不明" AS item_category
FROM
order_detail
) b
ON
a.order_id = b.order_id
GROUP BY
b.item_category;
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
---|---|---|---|---|---|---|---|---|---|---|---|
1 | SIMPLE | a | NULL | ALL | NULL | NULL | NULL | NULL | 6 | 100.00 | Using temporary |
1 | SIMPLE | order_detail | NULL | ref | order_id | order_id | 5 | テスト用.a.order_id | 1 | 100.00 | Using index |