MySqlで再帰クエリを使用してツリーノードからすべての子孫を取得するにはどうすればよいですか?
これはMySqlにとって本当に問題であり、この質問の重要なポイントですが、まだいくつかの選択肢があります。
あなたがそのようなサンプルデータを持っていると仮定すると、あなたのサンプルほどではありませんが、実証するのに十分です:
create table treeNode(
id int, parent_id int, name varchar(10), type varchar(10),level int);
insert into treeNode
(id, parent_id, name, type, level) values
( 1, 0, 'C1 ', 'CATEGORY', 1),
( 2, 1, 'C1.1 ', 'CATEGORY', 2),
( 3, 2, 'C1.1.1', 'CATEGORY', 3),
( 4, 1, 'C1.2 ', 'CATEGORY', 2),
( 5, 4, 'C1.2.1', 'CATEGORY', 3),
( 3, 8, 'G1.1.1', 'GROUP', 3),
( 4, 9, 'G1.2 ', 'GROUP', 2),
( 5, 4, 'G1.2.1', 'GROUP', 3),
( 8, 9, 'G1.1 ', 'GROUP', 2),
( 9, 0, 'G1 ', 'GROUP', 1);
最初の選択肢:レベルコード
treeNodeテーブルのname列のサンプルデータのように。 (英語での言い方がわかりません。level code
の正しい表現についてコメントしてください。 。)
C1
のすべての子孫を取得するには またはG1
このように単純な場合があります:
select * from treeNode where type = 'CATEGORY' and name like 'C1%' ;
select * from treeNode where type = 'GROUP' and name like 'G1%' ;
私はこのアプローチを非常に好みます。treeNodeをアプリケーションに保存する前にこれらのコードを生成する必要さえあります。レコード数が多い場合は、再帰クエリやプロシージャよりも効率的です。これは非正規化の良いアプローチだと思います。
このアプローチでは、ステートメント 参加が必要 可能性があります:
SELECT distinct p.* --if there is only one tree node for a product, distinct is not needed
FROM product p
JOIN product_type pt
ON pt.id= p.parent_id -- to get product type of a product
JOIN linked_TreeNode LC
ON LC.product_id= p.id -- to get tree_nodes related to a product
JOIN (select * from treeNode where type = 'CATEGORY' and name like 'C1%' ) C --may replace C1% to concat('$selected_cat_name','%')
ON LC.treeNode_id = C.id
JOIN (select * from treeNode where type = 'GROUP' and name like 'G1%' ) G --may replace G1% to concat('$selected_group_name','%')
ON LC.treeNode_id = G.id
WHERE pt.name = '$selected_type' -- filter selected product type, assuming using product.name, if using product.parent_id, can save one join by pt like your original sql
甘いですね。
2番目の選択肢:レベル番号
DDLに示されているように、treeNodeテーブルにレベル列を追加します。
レベル番号は、レベルコードよりも保守がはるかに簡単です。 アプリケーションで。
C1
のすべての子孫を取得するためのレベル番号付き またはG1
このようなちょっとしたトリックが必要です:
SELECT id, parent_id, name, type, @pv:=concat(@pv,',',id) as link_ids
FROM (select * from treeNode where type = 'CATEGORY' order by level) as t
JOIN (select @pv:='1')tmp
WHERE find_in_set(parent_id,@pv)
OR find_in_set(id,@pv);
-- get all descendants of `C1`
SELECT id, parent_id, name, type, @pv:=concat(@pv,',',id) as link_ids
FROM (select * from treeNode where type = 'GROUP' order by level) as t
JOIN (select @pv:=',9,')tmp
WHERE find_in_set(parent_id,@pv)
OR find_in_set(id,@pv) ;
このアプローチは最初のアプローチよりも低速ですが、再帰クエリよりも高速です。
質問への完全なSQLは省略されています。 CとGの2つのサブクエリを上記の2つのクエリに置き換えるだけです。
注:
ここなど、多くの同様のアプローチがあります。
、こちら
、またはここ
。レベル番号またはレベルコードで注文しない限り、機能しません。このorder by level
を変更する order by id
違いを確認します。
別の選択肢:入れ子集合モデル
この
ツリーノードテーブルに左の番号と右の番号を追加して、すべての子孫のIDをそれらの間に囲む必要があります。