如何以最有效的方式重用查询结果?
我有两个表:项目和关系。物品可以是孤独的物品,也可以是其他物品的孩子。关系在关系表中维护。项目由depNo,itemNo列唯一标识。以下是样本数据集:
create table items
(
itemId int identity (1,1) not null ,
depNo int not null,
itemNo int not null,
name varchar(50),
class int not null, -- 0 - unknown class, 1 - child item
constraint pk_depNo_itemNo primary key (depNo, itemNo)
);
create table relations
(
relId int identity (1,1) not null,
pDepNo int not null,
pItemNo int not null,
cDepNo int not null,
cItemNo int not null,
constraint pk_parent_child primary key (pDepNo, pItemNo, cDepNo, cItemNo)
);
insert into items values (1, 1, 'M1CItem1', 1);
insert into items values (1, 2, 'M1CItem2', 1);
insert into items values (1, 3, 'M1CItem3', 1);
insert into items values (2, 1, 'Master1', 0);
insert into items values (2, 2, 'LItem1', 0);
insert into items values (2, 3, 'LItem2', 0);
insert into items values (2, 4, 'LItem3', 0);
insert into items values (2, 5, 'Master2', 0);
insert into items values (2, 6, 'M2CItem1', 1);
insert into items values (2, 7, 'M2CItem1', 1);
insert into relations values (2, 1, 1, 1);
insert into relations values (2, 1, 1, 2);
insert into relations values (2, 1, 1, 3);
insert into relations values (2, 5, 2, 6);
insert into relations values (2, 5, 2, 7);
如果item是子项,则以下查询选择满足查询条件的所有项目或其父项:
with qRes as (
select depNo, itemNo, name, class, pDepNo, pItemNo from items
left outer join relations
on depNo = cDepNo
and itemNo = cItemNo
where name like '%Item1'
)
-- select all results where item is not a child
select depNo, itemNo, name, class from qRes where class <> 1
union
-- select all parents of the children
select B.depNo, B.itemNo, B.name, B.class from qRes A
inner join items B
on A.pDepNo = B.depNo
and A.pItemNo = B.itemNo;
执行的查询将返回:
depNo itemNo name class
2 1 Master1 0
2 2 LItem1 0
2 5 Master2 0
有没有更好的方法来解决这个问题?
答案 0 :(得分:1)
如果你只有一个级别的递归,那么你的方法很好,如果你在层次结构中有很多级别,那么你可能想要考虑使用递归方法。
e.g。如果我改变你的一个关系:
UPDATE Relations
SET pItemNo = 6
WHERE cItemNo = 7
然后,该行{DepNo: 2, ItemNo: 7, name: M2CItem1}
成为{DepNo: 2, ItemNo: 6, name: M2CItem1}
的孩子,而{DepNo: 2, ItemNo: 5, name: Master2}
的孩子又是M2CItem1
的孩子
以下内容将同时返回Master2
和;WITH CTE AS
( SELECT depNo, itemNo, name, class, pDepNo, pItemNo, 1 [RecursionLevel]
FROM items
LEFT JOIN relations
ON DepNo = cDepNo
AND ItemNo = cItemNo
WHERE name like '%Item1'
UNION ALL
SELECT i.depNo, i.itemNo, i.name, i.class, r.pDepNo, r.pItemNo, RecursionLevel + 1
FROM CTE i
INNER JOIN relations r
ON i.pDepNo = r.cDepNo
AND i.pItemNo = r.cItemNo
)
SELECT DISTINCT c.DepNo, c.ItemNo, i.Name, i.Class
FROM CTE c
INNER JOIN Items i
ON COALESCE(c.pDepNo, c.DepNo) = i.DepNo
AND COALESCE(c.pItemNo, c.ItemNo) = i.ItemNo
;WITH CTE AS
( SELECT depNo, itemNo, name, class, pDepNo, pItemNo, 1 [RecursionLevel]
FROM items
LEFT JOIN relations
ON DepNo = cDepNo
AND ItemNo = cItemNo
WHERE name like '%Item1'
UNION ALL
SELECT i.depNo, i.itemNo, i.name, i.class, r.pDepNo, r.pItemNo, RecursionLevel + 1
FROM CTE i
INNER JOIN relations r
ON i.pDepNo = r.cDepNo
AND i.pItemNo = r.cItemNo
), CTE2 AS
( SELECT c.DepNo, c.ItemNo, i.Name, i.Class, RecursionLevel, MAX(RecursionLevel) OVER(PARTITION BY c.DepNo, c.ItemNo) [MaxRecursionLevel]
FROM CTE c
INNER JOIN Items i
ON COALESCE(c.pDepNo, c.DepNo) = i.DepNo
AND COALESCE(c.pItemNo, c.ItemNo) = i.ItemNo
)
SELECT DepNo, ItemNo, Name, Class
FROM CTE2
WHERE Recursionlevel = maxRecursionLevel
但是,如果您只想返回最顶级的父级,则可以使用:
{DepNo: 2, ItemNo: 5, name: Master2}
这只会为行{DepNo: 2, ItemNo: 7, name: M2CItem1}
返回{{1}},因为这是其父级的父级。
Working Examples on SQL Fiddle
顺便说一句,我认为您应该重新考虑您的架构?如果您正在使ItemNo和DepNo成为复合主键,那么您的标识列是什么?你应该选择其中一个,而不是两个。
答案 1 :(得分:0)
您可以使用UNION ALL语句在公用表表达式本身中执行子父级递归,但您的方法几乎就在那里。
如果您可以选择更改表,则可能需要查看Hierarchy
数据类型。