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
Common Table Expressionsをサポートしない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
クエリ内の可変割り当て
このクエリは特定のMySQL構文を使用します。変数は実行中に割り当てられ、変更されます。実行の順序についていくつかの仮定があります:
-
from
句が最初に評価されます。ここで@pv
初期化されます。 -
where
句は、from
から取得した順序でレコードごとに評価されます。 エイリアス。したがって、これは、親が子孫ツリーにあるとすでに識別されているレコードのみを含むように条件が設定される場所です(プライマリ親のすべての子孫は、@pv
に段階的に追加されます。 。 - この
where
の条件 条項は順番に評価され、全体的な結果が確実になると評価は中断されます。したがって、2番目の条件はid
を追加するため、2番目に配置する必要があります 親リストに追加します。これは、id
の場合にのみ発生します。 最初の条件に合格します。長さコード> 関数は、
pv
であっても、この条件が常に真であることを確認するためにのみ呼び出されます。 文字列は何らかの理由で偽の値を生成します。
全体として、これらの仮定はリスクが高すぎて信頼できないと感じるかもしれません。 ドキュメント 警告:
期待どおりの結果が得られる可能性がありますが、これは保証されていません[...]ユーザー変数を含む式の評価の順序は定義されていません。
したがって、上記のクエリと一貫して機能する場合でも、たとえば条件を追加したり、このクエリをより大きなクエリのビューまたはサブクエリとして使用したりすると、評価の順序が変わる可能性があります。これは、が将来削除される「機能」です。 MySQLリリース :
MySQLの以前のリリースでは、
SET
以外のステートメントでユーザー変数に値を割り当てることができました。 。この機能は、下位互換性のためにMySQL 8.0でサポートされていますが、MySQLの将来のリリースで削除される可能性があります。
上記のように、MySQL 8.0以降では、再帰的な with
を使用する必要があります。 構文。
効率
find_in_set
操作は、リスト内の数値を見つけるための最も理想的な方法ではありません。確かに、返されるレコード数と同じ桁数のサイズに達するリスト内ではありません。
代替案1:再帰的
、 connect by
ますます多くのデータベースがWITH[RECURSIVE]<を実装しています。 / code> 構文
再帰クエリの場合(例: 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もサポートしています
。使用する構文については、この回答の上部を参照してください。
一部のデータベースには、 CONNECT BY
など、階層ルックアップ用の代替の非標準構文があります。 Oracle
で利用可能な句 、 DB2
、 Informix > 、 CUBRID
およびその他のデータベース。
MySQLバージョン5.7はそのような機能を提供していません。データベースエンジンがこの構文を提供する場合、または提供する構文に移行できる場合は、それが最適なオプションです。そうでない場合は、次の代替案も検討してください。
代替案2:パススタイルの識別子
id
を割り当てると、作業がはるかに簡単になります 階層情報を含む値:パス。たとえば、あなたの場合、これは次のようになります。
ID | 名前 |
---|---|
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
条件は、子孫を取得する親を指定します。必要に応じて、このクエリをより多くのレベルに拡張できます。