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

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

4

2 回答 2

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},因为这是其父级的父级。

SQL Fiddle 上的工作示例

顺便说一句,我认为您应该重新考虑您的架构?如果您将 ItemNo 和 DepNo 设为复合主键,您的身份列是什么?您可能应该选择其中之一,而不是两者兼而有之。

于 2012-07-10T09:21:31.633 回答
0

您可以使用 UNION ALL 语句在公用表表达式本身中进行子父递归,但您的方法几乎就在那里。

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

于 2012-07-10T07:54:52.650 回答