SQLのサブクエリ(副問合せ)についてまとめています。
SQLのサブクエリ(副問合せ)とは
別のSQLの結果を利用したSQLです。メインのSQLの中に別のselect文を埋め込むことで、埋め込んだselect文の結果をSQL内で利用できます。
【関連記事】
▶【SQL】初心者向け。副問い合わせ(サブクエリ)についての基本的な部分を解説。
以下は、departments(部署)テーブルから、社員数が5万人以下の部署を抽出するSQLです。WHERE句に5万人以上の部署名を抽出するSQLをサブクエリとして記述し、NOT INでそれ以外の部署名を抽出することで、5万人以下の部署名一覧を取得しています。
SELECT * FROM departments WHERE dept_name NOT IN (SELECT dept_name FROM departments INNER JOIN dept_emp ON dept_emp.dept_no = departments.dept_no GROUP BY dept_name HAVING Count(emp_no) > 50000); +---------+--------------------+ | dept_no | dept_name | +---------+--------------------+ | d009 | Customer Service | | d002 | Finance | | d003 | Human Resources | | d001 | Marketing | | d006 | Quality Management | | d008 | Research | +---------+--------------------+ 6 rows in set (2.15 sec)
【関連記事】
▶SQLで「~以外」を指定するには、NOT(否定)とサブクエリの組み合わせ
サブクエリ部分のSQLを単体で実行するとこうなります。社員数が5万人を超える部署、Development、Production、Salesが抽出されました。
SELECT dept_name FROM departments INNER JOIN dept_emp ON dept_emp.dept_no = departments.dept_no GROUP BY dept_name HAVING Count(emp_no) > 50000; +-------------+ | dept_name | +-------------+ | Development | | Production | | Sales | +-------------+ 3 rows in set (0.71 sec)
上記のSQLで得た結果を、おおもとのメインクエリの条件に当てはめて実行すると以下のようになります。サブクエリを使ったSQLと同一の結果になりました。
SELECT * FROM departments WHERE dept_name NOT IN ( 'Development', 'Production', 'Sales' ); +---------+--------------------+ | dept_no | dept_name | +---------+--------------------+ | d009 | Customer Service | | d002 | Finance | | d003 | Human Resources | | d001 | Marketing | | d006 | Quality Management | | d008 | Research | +---------+--------------------+ 6 rows in set (0.00 sec)
サブクエリのレスポンス低下を、実行計画で確認
サブクエリは、複数のSQLを同時実行するため、レスポンスが遅くなりがちです。実行計画を確認し、テーブルフルスキャンのような遅いクエリが組み込まれていないかどうか確認すると良いでしょう。
以下は、MySQLにて、explainで実行計画を確認した結果です。メインクエリ、サブクエリともに検索にキーが使われており、テーブルフルスキャンをおこなうようなクエリは混入していません。
+----+-------------+-------------+------------+-------+-------------------+-----------+---------+-------------------------------+-------+----------+--------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------------+------------+-------+-------------------+-----------+---------+-------------------------------+-------+----------+--------------------------+ | 1 | PRIMARY | departments | NULL | index | NULL | dept_name | 162 | NULL | 9 | 100.00 | Using where; Using index | | 2 | SUBQUERY | departments | NULL | index | PRIMARY,dept_name | dept_name | 162 | NULL | 9 | 100.00 | Using index | | 2 | SUBQUERY | dept_emp | NULL | ref | dept_no | dept_no | 16 | employees.departments.dept_no | 41392 | 100.00 | Using index | +----+-------------+-------------+------------+-------+-------------------+-----------+---------+-------------------------------+-------+----------+--------------------------+ 3 rows in set, 1 warning (0.00 sec)
サブクエリは、2つ以上のSQLを使うことも可能です。しかし、その場合SQLが読みづらくなります。withを使ってサブクエリを読みやすく記述することができます。
WITH cte1 AS (SELECT a, b FROM table1), cte2 AS (SELECT c, d FROM table2) SELECT b, d FROM cte1 JOIN cte2 WHERE cte1.a = cte2.c;
【関連記事】
▶SQLのwith句のサンプル サブクエリを共通テーブル式(CTE)を使って、単純なクエリで記述
上記の例では、「SELECT a, b FROM table1」と「SELECT c, d FROM table2」という2つのクエリをサブクエリとしてwith句で記述しています。
まとめ
- サブクエリ(副問合せ)は、別のSQLの結果を利用したSQL
- メインクエリに複数のサブクエリを記述可能だが、実行速度に注意
- with句を使って、サブクエリを見やすく記述できる