Как да получите всички наследници от дървовиден възел с рекурсивна заявка в 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. (Не знам как да го кажа на английски, коментирайте ме за правилния израз на 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
Сладко, нали?
Втори избор:номер на ниво
Добавете колона на ниво към таблицата treeNode, както е показано в DDL.
Номерът на ниво се поддържа много по-лесно от кода на ниво в приложението.
С номер на ниво за получаване на всички наследници на 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 с две заявки по-горе.
Забележка:
Има много подобни подходи като тук
, тук
, или дори тук
. Те няма да работят, освен ако не са подредени по номер на ниво или код на ниво. Можете да тествате последната заявка в този SqlFiddle
чрез промяна на order by level
за order by id
за да видите разликите.
Друг избор:Моделът на вложен набор
Моля, вижте този блог , още не съм тествал. Но мисля, че е подобно на последните два варианта.
Трябва да добавите ляво число и дясно число към таблицата с дървовидни възли, за да обградите идентификаторите на всички наследници между тях.