2012-07-10 23 views
0

如何以最有效的方式重用查询结果?如何从查询结果中递归地选择?

我有两个表:项目和关系。项目可以是孤独的项目,也可以是其他项目的子项目。关系维护在关系表中。项目由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);  

下面的查询选择的所有项目满足查询条件或者其父母,如果项目是一个小孩:

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 

有没有更好的方法来解决这个问题?

回答

1

如果你只有一层递归,那么你的方法很好,如果你在层次结构中有很多层次,那么你可能要考虑使用递归方法。

例如如果我改变你的人际关系之一:

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}

孩子下面将同时返回M2CItem1Master2

;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},因为它是其父项的父项。

Working Examples on SQL Fiddle

顺便说一句,我认为你应该重新考虑你的架构?如果您将ItemNo和DepNo作为复合主键,您的身份栏是什么?你应该选择一个或另一个,而不是两个。

0

您可以使用UNION ALL语句在公用表表达式本身中执行子父级递归,但您的方法非常多。

如果您可以选择更改表格,您可能需要查看Hierarchy数据类型。

+1

http://msdn.microsoft.com/en-us/library/ms186243(v=sql.105).aspx – Sean 2012-07-10 07:57:13