mysqlのサブクエリに問題があることはなんとなく見聞きしていたが今日はこれについて深く追求せざるを得ない機会があったので調べてみました。
自分なりに理解したので簡単にまとめてみるが、以下のブログの記事が素晴らしいので詳しくはそちらもあわせて読むと良いでしょう。

http://nippondanji.blogspot.com/2009/03/mysql_25.html

mysqlの実行計画を知るには

mysqlで実行計画を取得するには実行したいクエリーの前に EXPLAIN をつけるだけ。
実にシンプルだが、取得できるデータもシンプル極まりない(^_^;)

mysql> EXPLAIN SELECT COUNT(customer_id) FROM dtb_customer  WHERE  del_flg = 0  
AND customer_id IN (SELECT customer_id FROM dtb_order WHERE order_id IN 
(SELECT order_id FROM dtb_order_detail WHERE product_name LIKE '%とうもろこし%' ));
+----+--------------------+------------------+------+---------------+------+---------+------+------+-------------+
| id | select_type        | table            | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+--------------------+------------------+------+---------------+------+---------+------+------+-------------+
|  1 | PRIMARY            | dtb_customer     | ALL  | NULL          | NULL | NULL    | NULL | 1834 | Using where |
|  2 | DEPENDENT SUBQUERY | dtb_order        | ALL  | NULL          | NULL | NULL    | NULL | 2023 | Using where |
|  3 | DEPENDENT SUBQUERY | dtb_order_detail | ALL  | NULL          | NULL | NULL    | NULL | 2163 | Using where |
+----+--------------------+------------------+------+---------------+------+---------+------+------+-------------+
3 rows in set (0.02 sec)

実行計画の読み取り方は こちら に詳しく載っています。

IN句はEXISTS句におきかわる

さて、上にあげたクエリーは IN句に二重にサブクエリーが入っている、ツッコミどころ満載のSQLである。(実はEC-CUBEの中でこのクエリーが使われている。)
普通は1番内側のサブクエリーから実行されていって結果が返ってくるという動きを想像してしまうしそのほうが効率がいいのは明らかだ。
ところがmysqlでは外側のクエリーから実行し、得たレコードについて内側のサブクエリーを繰り返し実行するという処理になる。
つまりIN句なしで検索した結果が3000行ある場合、その各行についてサブクエリーの走査が実行するされるのでとんでもなく時間がかかってしまう。

何故このようになるかというとmysqlのオプティマイザがIN句をEXIST句に置き換えてしまうため、サブクエリーが相関サブクエリーとなり、結果上に述べた実行計画になってしまう。

DEPENDENT SUBQUERY に注意

このように実行計画で DEPENDENT SUBQUERY となっているものについては、 nxn の走査が走ってしまう可能性があるのでできるだけ排除したほうがいいだろう。
できる限りJOINを使うように心がけたい。

コメントは受け付けていません。