self join はただのエイリアス付き JOIN
sqlite の self join に特別な仕掛けはありません。両側が同じテーブルになっただけの普通の JOIN です。ポイントは、SQLite が 2 つのコピーを区別できるよう、それぞれにエイリアスを付けてやることだけ。
self join の出番は、テーブルの中の行が「同じテーブル内の別の行」を参照しているときです。定番は employees テーブルで、各行に上司を指す manager_id が入っているケースですね:
Adaには上司がいません。BorisとCleoはAdaの部下、DiegoとEsmeはBorisの部下です。この関係はすべて1つのテーブルの中で完結しています。まさに self join(自己結合)が真価を発揮する場面ですね。
self join の基本形
各社員と上司の名前をペアで取り出すには、employees テーブルを自分自身と結合します。同じテーブルを2回登場させ、片方を「社員」役、もう片方を「上司」役として扱うのがポイントです。
ストレージを共有しているだけの 2 つのテーブルとして読むのがコツです。e は従業員側の行、m は上司側の行を表します。結合条件の e.manager_id = m.id がこの 2 つを紐づけていて、各従業員に対して、その manager_id に一致する id を持つ行を m 側から探してくる、という動きになります。
ここで Ada が結果に出てこなかったことに注目してください。彼女の manager_id は NULL なので、一致しない行を切り捨てる INNER JOIN の挙動によって落とされています。
マッチしない行も残したいときは LEFT JOIN
上司がいない人も含めて全員を結果に残したい場合は、LEFT JOIN に切り替えます。
今度は Ada が manager 列に NULL を持った状態で出てきます。仕組み自体は同じ自己結合(self join)ですが、結合の種類が変わったことで LEFT JOIN 本来の動き ―― 左側の行は全部残して、対応がないところは空欄で埋める ―― がそのまま効いています。
人物一覧を表示したいときは、たいていこちらの形が欲しくなります。「上司なし」という情報自体に意味があるので、行ごと消してしまうのはもったいないんですよね。
エイリアスは省略できません
試しにエイリアスなしで self join を書いてみると、SQLite はもう何を指しているのか判断できません:
SELECT name, manager_id FROM employees JOIN employees ON manager_id = id;
-- エラー: 列名があいまいです: name
各カラムがテーブルの両方のコピーから1回ずつ、合計2回登場するため、SQLite はどちらを指しているのか判断できません。これを解決するのがエイリアスで、それぞれのコピーに別々の名前を付けてあげます。エイリアスは、テーブル名そのままではなく、その行が担う役割が伝わる名前を選ぶのがコツです。
- 社員と上司なら
eとm - 階層構造なら
parentとchild - 任意のペアを比較するなら
aとb
self join が読みやすいコードになるかどうかは、ほぼエイリアス次第と言っていいくらいです。
同じテーブル内でペアを探す
self join は階層データ専用というわけではありません。同じテーブル内の行同士を比較したい場面なら、どんなときでもこのパターンが活きてきます。たとえば次のような商品リストから、価格が同じ商品の組み合わせをすべて抽出したいケースを見てみましょう。
注目すべきポイントは2つあります。まず、a.price = b.price が実際のマッチング条件です。次に、a.id < b.id は、同じペアが2回返ってくる((マグカップ, ノート) と (ノート, マグカップ) の両方)のを防ぎ、さらに同じ行同士のペアリングも除外しています。この < のテクニックはペアを列挙する場面で繰り返し登場するので、覚えておくと便利です。
2階層上をたどる
self joinは階層を1段階だけ遡るのに使えます。では、各社員の「上司の上司」を取得したい場合はどうするか? テーブルを3回結合すればOKです。
新しいエイリアスを追加するたびに、ツリーを1階層さかのぼれるイメージです。2〜3階層くらいなら問題なく動きますが、それ以上になると一気に破綻します。クエリを書く時点で階層の深さを知っておく必要があり、しかも階層ごとに JOIN を足さなければなりません。この限界を突破するために生まれたのが、再帰 CTE です。
self join を使わない方がいい場面
self join が真価を発揮するのは、関係の両側にある列を結果に含めたいときです。ただ単に絞り込みたいだけ——たとえば「Ada が上司である社員を全員出したい」——という場合は、サブクエリの方が読みやすくなることが多いです。
エイリアスでこねくり回す必要もなく、意図もハッキリします。判断の目安はこうです。両方の行のデータを出力に含めたいなら self join、比較用の値が欲しいだけならサブクエリ。
ただし、深さが決まらない階層構造(組織図、ファイルツリー、スレッド型コメントなど)になると、どちらのパターンもスケールしません。そこは再帰CTEの出番です。
次は: サブクエリ
self join とサブクエリは解決できる問題が重なる部分があり、どちらを選ぶべきか分かっていると、後で SQL を眺めて頭を抱えずに済みます。次のページではサブクエリを深掘りします — スカラー、相関、IN 形式まで、それぞれが活きる場面を見ていきましょう。
よくある質問
SQLiteのself joinとは何ですか?
self join(自己結合)は、同じテーブル同士をJOINする書き方のことです。同一のテーブルに2つの異なるエイリアスを付けることで、SQLiteはそれぞれを別の行ソースとして扱えるようになります。あとは、ある行と別の行を結びつけるカラム(典型的には社員と上司のような親子関係)でマッチさせます。
なぜself joinではエイリアスが必須なのですか?
エイリアスを付けないと、同じカラム名を書いたときにSQLiteがどちらのテーブルを指しているのか判断できません。例えば社員側にe、上司側にmというエイリアスを付けておけば、e.manager_id = m.idのように曖昧さなく書けます。これは省略可能なオプションではなく、付けないとそもそもクエリがパースできません。
self joinとサブクエリはどう使い分ければよいですか?
結果に両方の行のカラムを並べたいとき(例:社員の名前と上司の名前を同じ行に出したい)はself joinが向いています。一方、単に絞り込みや単一値の参照だけならサブクエリで十分です。なお、組織図のような深い階層を辿りたい場合はどちらも力不足なので、再帰CTE(WITH RECURSIVE)の出番になります。