では、さっそく両選手の入場です!まずはそれぞれの選手の持つ技と特徴を簡単にご紹介していきます。
テーブル連結。INNER JOIN、LEFT JOIN、RIGHT JOIN、FULL OUTER JOINなど多彩な技を持つ。
複数のテーブルを関連キーでガッチリ結合!関連する情報をまとめて複数のテーブルから引き出す力持ちです。リレーショナルデータベースの真骨頂、テーブル間の「関係性」を利用してデータを操作するのが大得意です!
入れ子構造。SELECT句、FROM句、WHERE句、HAVING句など、SQL文のあらゆる場所に潜り込み、華麗にデータ操作を実現します!
SQL文の中に、もう一つのSQL文を忍ばせるトリッキーな技を使います。マトリョーシカ人形のように、処理を段階的に実行したり、複雑な条件を指定したりするのが得意です。
JOIN選手とサブクエリ選手、どちらもSQLを書く上で欠かせない、頼れる存在です。それでは、いよいよ対決のゴングを鳴らしましょう!
それでは、いよいよ対決開始です!4つのよくあるシチュエーションで、どちらの書き方がよりよいのか判定していきましょう!
お題
社員テーブル(Employees)と部署テーブル(Departments)を結合して、社員名とその所属部署名の一覧を取得する。基本中の基本ですね!
では、両選手の戦い方を見ていきましょう。
SELECT
e.employee_name,
d.department_name
FROM
Employees e
INNER JOIN
Departments d ON e.department_id = d.department_id;
SELECT
e.employee_name,
(SELECT d.department_name FROM Departments d WHERE d.department_id = e.department_id) AS department_name
FROM
Employees e;
JOIN選手の勝利!
このような単純なテーブル結合の場合、一般的にJOINの方がシンプルで、「テーブルを結びつけている」という意図がSQLから明確に伝わります。また、多くのデータベースシステムでは、JOINの方が、SQL実行の司令塔であるオプティマイザが処理計画を立てやすく、パフォーマンスが良い傾向があります。
サブクエリの中でも、特にSELECT句などで利用されるスカラ・サブクエリは、行ごとに処理が実行されるイメージのため、データ量が多い場合に遅くなる可能性があります。
お題
社員情報・給与情報を含む社員テーブル(Employees)から全社員の平均給与を計算し、その平均給与よりも高い給与をもらっている社員の情報を取得したい。集計結果を使うパターンです。
SELECT
e.*
FROM
Employees e
JOIN
(SELECT AVG(salary) AS avg_salary FROM Employees) avg_table
ON e.salary > avg_table.avg_salary;
SELECT
*
FROM
Employees
WHERE
salary > (SELECT AVG(salary) FROM Employees);
サブクエリ選手の勝利!
このケースでは、WHERE句にサブクエリを書く方が「給与が平均給与よりも大きい」という条件を非常にストレートに表現できており、可読性が高いと言えます。
JOINを使った書き方も間違いではありませんが、平均給与を計算するためだけの一時的なテーブル(avg_table)を作る必要があり、少し回りくどい印象を受けるかもしれません。
お題
顧客テーブル(Customers)と注文履歴テーブル(Orders)があり、特定の商品(商品IDが’A001’の商品)を購入したことがある顧客のリストを取得したい。「〜したことがあるかどうか」を知りたい場面です。
SELECT DISTINCT
c.customer_id,
c.customer_name
FROM
Customers c
INNER JOIN
Orders o ON c.customer_id = o.customer_id
WHERE
o.product_id = ‘A001’;
SELECT
c.customer_id,
c.customer_name
FROM
Customers c
WHERE
EXISTS (
SELECT 1
FROM Orders o
WHERE o.customer_id = c.customer_id
AND o.product_id = ‘A001’
);
サブクエリ選手の勝利!
EXISTSは、サブクエリ内の条件に一致する行が 1件でも見つかった時点 でTRUEを返し、その後の検索を打ち切るため、非常に効率的に動作することが多いです。
一方、JOINでは条件に合うすべての行を結合するうえに、このケースでは重複排除(DISTINCT)のコストもかかる場合があります。パフォーマンス面でEXISTSに軍配が上がるという判定が妥当であるといえるでしょう。
お題
社員テーブル(Employees)には、各社員の情報に加え、その社員の上司にあたる人の社員番号(manager_id)が格納されています。全社員について、「社員名」とその「直属の上司の名前」を一覧で表示したい、という状況です。社長などの上司がいない社員は上司名をNULLで表示します。
SELECT
e.employee_name,
m.employee_name
FROM
Employees e
LEFT JOIN
Employees m ON e.manager_id = m.employee_id;
SELECT
e.employee_name,
(
SELECT m.employee_name
FROM Employees m
WHERE m.employee_id = e.manager_id
)
FROM
Employees e;
JOIN選手の勝利!
このように「同じテーブル内にあるレコード同士の関係性」(親子関係、階層関係など)を扱いたい場合、自己結合が最も標準的で、意図が明確になります。サブクエリでも同じ結果は得られますが、各社員レコードに対して上司の名前を個別に検索するような形になり、多くの場合JOINよりもパフォーマンスが劣る可能性があります。
勝負の結果をご覧いただいた通り、JOINとサブクエリは「常にJOINが良い」「常にサブクエリが良い」という単純な話ではないことがお分かりいただけたかと思います。
では、日々の業務でどちらを使うか迷ったとき、結局のところどのように考えていけばよいのでしょうか?迷った時の考え方を説明していきます。
まずは、自分が「書きやすい」「直感的によい」と感じる方で、動くSQLを書いてみましょう。
もし処理対象のデータ量が多かったり、レスポンス速度が求められたりするなら、実行計画を確認しましょう。ボトルネックになっている箇所を特定し、必要であればJOINとサブクエリの書き換えを検討します。
少し時間を置き、あらためてSQLを読んだ時に、そのSQLが何をしようとしているのかを直観的に理解できるかを確認しましょう。分かりにくい場合は、WITH句を使うなど、リファクタリングを検討します。
「JOIN vs サブクエリ」対決、楽しんでいただけましたでしょうか?
JOINもサブクエリも、それぞれに得意な土俵があり、どちらもSQLコーディングには欠かせないテクニックです。それぞれの特徴、メリット・デメリットを理解し、目の前にある課題や状況に応じて使い分けることが、よりよいSQLを書くための鍵となります。
パフォーマンスと可読性、時にはその両立に頭を悩ませることもあるかと思いますが、それもSQLの面白さでもあります。本記事の内容を参考にすることで、皆さんのSQLライフがより快適になることを願っています!
木曜日の夕方に仕事で疲れた脳みそをリフレッシュしたいとき、一人でゆっくりしたい夜、ちょっとした空き時間に、気軽に「つまめる」ソースコードの話題をお届けします。
まるで隠れ家バーでマスターが語るウンチクのように、普段は見過ごしがちなコードの奥深さや、思わず「へぇ〜!」と唸るようなユニークなアイデア、クスッと笑える小ネタを、ソースコードの世界を熟知した「情シスのじかん」がご紹介します。
コードを書くのが大好きなエンジニアさんも、ソースコードはちょっと苦手…という情シス部門の方も、この特集を読めば、きっとソースコードの新たな一面を発見できるはず。
業務効率化のヒントになるTipsや、セキュリティ対策に役立つ情報も盛りだくさん。
さあ、あなたも「おつまみとしてのソースコード」で、技術の世界をもっと身近に、もっと楽しく感じてみませんか?
本特集はこちら30秒で理解!フォローして『1日1記事』インプットしよう!