MSSQL 2005で再帰クエリを作成するにはどうすればよいですか?
-
04-07-2019 - |
質問
次の表があるとします:
CustomerID ParentID Name
========== ======== ====
1 null John
2 1 James
3 2 Jenna
4 3 Jennifer
5 3 Peter
6 5 Alice
7 5 Steve
8 1 Larry
1つのクエリで、Jamesのすべての子孫(Jenna、Jennifer、Peter、Alice、Steve)を取得します。 おかげで、 パブロ。
解決
SQL Server 2005では、 CTE(一般的なテーブル式)を使用できます。
with Hierachy(CustomerID, ParentID, Name, Level)
as
(
select CustomerID, ParentID, Name, 0 as Level
from Customers c
where c.CustomerID = 2 -- insert parameter here
union all
select c.CustomerID, c.ParentID, c.Name, ch.Level + 1
from Customers c
inner join Hierachy ch
on c.ParentId = ch.CustomerID
)
select CustomerID, ParentID, Name
from Hierachy
where Level > 0
他のヒント
ボトムアップには、マティウスの答えを少し修正して使用します:
with Hierachy(CustomerID, ParentID, Name, Level)
as
(
select CustomerID, ParentID, Name, 0 as Level
from Customers c
where c.CustomerID = 2 -- insert parameter here
union all
select c.CustomerID, c.ParentID, c.Name, ch.Level + 1
from Customers c
inner join Hierachy ch
-- EDITED HERE --
on ch.ParentId = c.CustomerID
-----------------
)
select CustomerID, ParentID, Name
from Hierachy
where Level > 0
ストアドプロシージャがなければ、SQLで再帰を行うことはできません。これを解決する方法は、ネストされたセットを使用することです。彼らは基本的にSQLでツリーをセットとしてモデル化します。
現在のデータモデルを変更するか、場合によっては元のモデルにビューを作成する方法を考え出す必要があることに注意してください。
Postgresqlの例(ごく少数のpostgresql拡張機能、SERIALおよびON COMMIT DROPを使用すると、ほとんどのRDBMSに同様の機能があります):
セットアップ:
CREATE TABLE objects(
id SERIAL PRIMARY KEY,
name TEXT,
lft INT,
rgt INT
);
INSERT INTO objects(name, lft, rgt) VALUES('The root of the tree', 1, 2);
子の追加:
START TRANSACTION;
-- postgresql doesn't support variables so we create a temporary table that
-- gets deleted after the transaction has finished.
CREATE TEMP TABLE left_tmp(
lft INT
) ON COMMIT DROP; -- not standard sql
-- store the left of the parent for later use
INSERT INTO left_tmp (lft) VALUES((SELECT lft FROM objects WHERE name = 'The parent of the newly inserted node'));
-- move all the children already in the set to the right
-- to make room for the new child
UPDATE objects SET rgt = rgt + 2 WHERE rgt > (SELECT lft FROM left_tmp LIMIT 1);
UPDATE objects SET lft = lft + 2 WHERE lft > (SELECT lft FROM left_tmp LIMIT 1);
-- insert the new child
INSERT INTO objects(name, lft, rgt) VALUES(
'The name of the newly inserted node',
(SELECT lft + 1 FROM left_tmp LIMIT 1),
(SELECT lft + 2 FROM left_tmp LIMIT 1)
);
COMMIT;
下から上に証跡を表示します:
SELECT
parent.id, parent.lft
FROM
objects AS current_node
INNER JOIN
objects AS parent
ON
current_node.lft BETWEEN parent.lft AND parent.rgt
WHERE
current_node.name = 'The name of the deepest child'
ORDER BY
parent.lft;
ツリー全体を表示します:
SELECT
REPEAT(' ', CAST((COUNT(parent.id) - 1) AS INT)) || '- ' || current_node.name AS indented_name
FROM
objects current_node
INNER JOIN
objects parent
ON
current_node.lft BETWEEN parent.lft AND parent.rgt
GROUP BY
current_node.name,
current_node.lft
ORDER BY
current_node.lft;
ツリーの特定の要素からすべてを選択します:
SELECT
current_node.name AS node_name
FROM
objects current_node
INNER JOIN
objects parent
ON
current_node.lft BETWEEN parent.lft AND parent.rgt
AND
parent.name = 'child'
GROUP BY
current_node.name,
current_node.lft
ORDER BY
current_node.lft;
何かが足りない限り、再帰は必要ありません...
SELECT d.NAME FROM Customers As d
INNER JOIN Customers As p ON p.CustomerID = d.ParentID
WHERE p.Name = 'James'
所属していません StackOverflow