2012-07-10 37 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