0

最も効率的な方法でクエリ結果を再利用するにはどうすればよいでしょうか?

アイテムとリレーションの 2 つのテーブルがあります。アイテムは、単独のアイテムである場合もあれば、別のアイテムの子である場合もあります。関係は関係テーブルで維持されます。アイテムは、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

再帰のレベルが 1 つしかない場合は、アプローチは問題ありません。階層内に多くのレベルがある場合は、再帰的アプローチの使用を検討することをお勧めします。

たとえば、あなたの関係の 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}

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}

SQL Fiddle での作業例

余談ですが、スキーマを再考する必要があると思いますか? ItemNo と DepNo を複合主キーにする場合、ID 列は何ですか? おそらく、両方ではなく、どちらかを選択する必要があります。

于 2012-07-10T09:21:31.633 に答える
0

UNION ALL ステートメントを使用して、共通テーブル式自体で親子再帰を実行できますが、アプローチはほとんどありません。

Hierarchyテーブルを変更するオプションがある場合は、データ型を確認することをお勧めします。

于 2012-07-10T07:54:52.650 に答える