我有以下DB-Structure / hierarchy:
表 product_type :
id,name, ....
表产品:
id,parent_id, name, ...
parent_id :是product_type id
表 treeNode :
id, parent_id, name, type
它的树层次结构(根有n个子节点) 层次结构的级别数未知。
col type
的值为" CATEGORY" 或" GROUP" ,
这意味着,我有2棵树:
分类
TreeNode 1
sub 1.1
sub.1.1.1
sub.1.1.2
....
sub 1.2
sub.1.2.1
sub.1.2.2
....
TreeNode 2
sub 2.1
sub.2.1.1
sub.2.1.2
....
sub 2.2
sub.2.2.1
sub.2.2.2
....
组:
TreeNode 1
sub 1.1
sub.1.1.1
sub.1.1.2
....
sub 1.2
sub.1.2.1
sub.1.2.2
....
TreeNode 2
sub 2.1
sub.2.1.1
sub.2.1.2
....
sub 2.2
sub.2.2.1
sub.2.2.2
....
表 linked_treeNode:
product_id, treeNode_id
现在说,用户选择:
1:产品类型(参数:$selected_type
)
2:一个类别(参数:$selected_cat
)
3:一个组(param:$selected_group
)
现在我想显示符合以下选项的所有产品:
1-)链接到选定的Catagory或其子类别
AND
2-)链接到选定的组或其子组
和
3-)链接到选定的产品类型
MySQL 语句(1语句)是什么?
我试过这个:
SELECT P.* FROM
product P, treeNode C, treeNode G, linked_TreeNode LC
WHERE
p.parent_id='$selected_type'
AND
(
C.type='CATEGORY'
AND
C.parent_id='$selected_cat'
AND
P.id=LC.product_id
AND
(LC.treeNode_id=C.id OR LC.treeNode_id='$selected_cat')
)
AND
(
G.type='GROUP'
AND
G.parent_id='$selected_group'
AND
P.id=LC.product_id
AND
(LC.treeNode_id=G.id OR LC.treeNode_id='$selected_group')
)
;
但我总是得到0结果!
我尝试了许多其他声明(更改),使用JOINS ..等等。但没有成功。
非常感谢编辑:我上面使用的声明错误,所以不要使用它!
答案 0 :(得分:1)
对于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
甜蜜,不是吗?
将一个级别列附加到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子查询。
注意:强>
有许多类似的方法,例如here,here,甚至here。除非按级别编号或级别代码排序,否则他们不会工作。您可以通过将order by level
更改为order by id
来查看此 SqlFiddle 中的最后一个查询,以查看差异。
请参考此blog,我还没有测试过。但我认为这与前两种选择相似。
需要在treenode表中添加左数和右数以包含所有后代'他们之间的ids。
答案 1 :(得分:0)
这在MySQL中是行不通的,因为它缺少你需要的功能:递归查询。
Oracle可以使用START WITH ... CONNECT BY语句执行此操作。
您对过程中的表进行递归并将结果写入临时表。可以在同一会话中查询该表。
类似的东西:
CREATE PROCEDURE products_by_cat_and_grp(typ INT, cat INT, grp INT)
BEGIN
-- create temporary table which we query later on
CREATE TEMPORARY TABLE tmp_products LIKE product;
ALTER TABLE tmp_products
ADD cat_id INT
, ADD grp_id INT;
-- first insert all products of the category and group
INSERT INTO tmp_products
SELECT P.*, cat.id, grp.id
FROM linked_TreeNode lc
JOIN product prd
ON lc.product_id = prd.id
JOIN treeNode cat
ON lc.treeNode_id = cat.id
JOIN treeNode grp
ON lc.treeNode_id = grp.id
WHERE prd.parent_id = typ
AND cat.id = cat
AND grp.id = grp;
-- now we iterate over subcategories until there is nothing left
SET @rownum = 1;
WHILE @rownum > 0 DO
CREATE TEMPORARY TABLE tmp_parents
AS SELECT DISTINCT id, cat_id AS parent_id
FROM tmp_products
UNION SELECT DISTINCT id, grp_id AS parent_id
FROM tmp_products;
INSERT INTO tmp_products
SELECT P.*, cat.id, grp.id
FROM linked_TreeNode lc
JOIN treeNode tn
JOIN product prd
ON lc.product_id = prd.id
JOIN treeNode cat
ON lc.treeNode_id = cat.id
JOIN treeNode grp
ON lc.treeNode_id = grp.id
JOIN tmp_parents par
ON (par.parent_id = cat.parent_id
OR par.parent_id = grp.parent_id)
AND par.id <> lc.product_id
WHERE prd.parent_id = typ;
-- see how many rows were inserted. If this becomes zero, the recursion is complete
SET @rownum = ROW_COUNT();
END WHILE;
SELECT * FROM tmp_products;
END$$
这未经过调整或测试,我也不会推荐它,因为查询返回可能需要很长时间。