以下のためのMySQL 8+:再帰的な使用with
構文を。
以下のためのMySQL 5.xの:使用のインライン変数、パスID、または自己結合します。
MySQL 8以降
with recursive cte (id, name, parent_id) as (
select id,
name,
parent_id
from products
where parent_id = 19
union all
select p.id,
p.name,
p.parent_id
from products p
inner join cte
on p.parent_id = cte.id
)
select * from cte;
で指定された値は、すべての子孫を選択する親のにparent_id = 19
設定する必要がid
あります。
MySQL 5.x
共通テーブル式をサポートしないMySQLバージョン(バージョン5.7まで)では、次のクエリでこれを実現します。
select id,
name,
parent_id
from (select * from products
order by parent_id, id) products_sorted,
(select @pv := '19') initialisation
where find_in_set(parent_id, @pv)
and length(@pv := concat(@pv, ',', id))
こちらがフィドルです。
ここで、で指定された値は、すべての子孫を選択する親のに@pv := '19'
設定する必要がid
あります。
これは、親が複数の子を持つ場合にも機能します。ただし、各レコードは条件を満たす必要parent_id < id
があります。そうでない場合、結果は完全ではありません。
クエリ内の変数の割り当て
このクエリは特定のMySQL構文を使用します。変数はその実行中に割り当てられ、変更されます。実行の順序についていくつかの仮定が行われます。
from
句が最初に評価されます。これ@pv
が初期化される場所です。
where
句がから検索のための各レコードについて評価されるfrom
エイリアス。したがって、ここでは、子孫ツリーにあると親が既に識別されているレコードのみが含まれるように条件が設定されます(プライマリ親のすべての子孫がに徐々に追加されます@pv
)。
- この
where
句の条件は順番に評価され、全体の結果が確定すると評価は中断されます。したがって、2番目の条件id
はを親リストに追加するため、2番目の条件にする必要があります。これは、id
が最初の条件を通過した場合にのみ発生します。length
機能のみを確認してください、この条件があっても、常に真であることを確認するために呼び出されpv
た文字列が何らかの理由でfalsy値をもたらすであろう。
全体として、これらの仮定は信頼できないほど信頼できない場合があります。ドキュメントは警告しています:
期待どおりの結果が得られるかもしれませんが、これは保証されていません[...]ユーザー変数を含む式の評価順序は未定義です。
したがって、上記のクエリと一貫して機能しますが、たとえば条件を追加したり、このクエリをより大きなクエリのビューまたはサブクエリとして使用したりすると、評価順序が変更される可能性があります。これは、将来のMySQLリリースで削除される「機能」です。
MySQLの以前のリリースでは、以外のステートメントでユーザー変数に値を割り当てることが可能SET
でした。この機能は、下位互換性のためにMySQL 8.0でサポートされていますが、MySQLの将来のリリースでは削除される可能性があります。
上記のように、MySQL 8.0以降では、再帰with
構文を使用する必要があります。
効率
非常に大きなデータセットの場合find_in_set
、リスト内の数値を検索する最も理想的な方法ではなく、返されるレコード数と同じ桁のサイズに達するリスト内ではないため、このソリューションは遅くなる可能性があります。
オルタナティブ1: with recursive
、connect by
より多くのデータベースが実装するSQL:1999 ISO標準WITH [RECURSIVE]
構文再帰クエリのために(例えばPostgresの8.4+、SQL Serverの2005+、DB2、Oracleの11gR2の+、SQLiteの3.8.4+、Firebirdの2.1+、H2、HyperSQL 2.1.0+、Teradataの、MariaDB 10.2.2以降)。また、バージョン8.0以降、MySQLでもサポートされています。使用する構文については、この回答の上部を参照してください。
一部のデータベースには、Oracle、DB2、Informix、CUBRID、およびその他のデータベースでCONNECT BY
使用可能な句など、階層ルックアップ用の代替の非標準構文があります。
MySQLバージョン5.7はそのような機能を提供していません。データベースエンジンがこの構文を提供する場合、または提供する構文に移行できる場合、それは確かに最適なオプションです。そうでない場合は、次の代替案も検討してください。
代替2:パススタイルの識別子
id
階層情報を含む値、つまりパスを割り当てると、物事がずっと簡単になります。たとえば、あなたの場合、これは次のようになります:
ID | NAME
19 | category1
19/1 | category2
19/1/1 | category3
19/1/1/1 | category4
次に、あなたselect
はこのようになります:
select id,
name
from products
where id like '19/%'
代替3:繰り返し自己結合
階層ツリーの深さの上限がわかっている場合は、次のsql
ような標準クエリを使用できます。
select p6.parent_id as parent6_id,
p5.parent_id as parent5_id,
p4.parent_id as parent4_id,
p3.parent_id as parent3_id,
p2.parent_id as parent2_id,
p1.parent_id as parent_id,
p1.id as product_id,
p1.name
from products p1
left join products p2 on p2.id = p1.parent_id
left join products p3 on p3.id = p2.parent_id
left join products p4 on p4.id = p3.parent_id
left join products p5 on p5.id = p4.parent_id
left join products p6 on p6.id = p5.parent_id
where 19 in (p1.parent_id,
p2.parent_id,
p3.parent_id,
p4.parent_id,
p5.parent_id,
p6.parent_id)
order by 1, 2, 3, 4, 5, 6, 7;
このフィドルを見る
where
あなたがの子孫を取得したい親の条件を指定します。必要に応じて、このクエリをより多くのレベルで拡張できます。