2008-10-27 24 views
24

Hãy nói rằng tôi có bảng sau:Làm cách nào để tạo truy vấn đệ quy trong MSSQL 2005?

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 

Tôi muốn lấy trong một truy vấn tất cả các hậu duệ của James (Jenna, Jennifer, Peter, Alice, Steve). Cảm ơn, Pablo.

+0

Trong gì RDBMS giải pháp nên chạy? Nếu đó là Oracle, sau đó tìm hiểu về CONNECT BY PRIOR – Salamander2007

+0

xin lỗi, quên đề cập đến, trong MSSQL 2005 –

Trả lời

34

Mở SQL Server 2005, bạn có thể sử dụng CTEs (Common Table Expressions):

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 
+0

Nếu bạn không muốn sử dụng đệ quy hoặc bạn cần phải có được cha mẹ từ một đứa trẻ, bạn có thể thực hiện các giải pháp từ bài viết của Rob Volk trên sqlteam: http://www.sqlteam.com/article/more-trees-hierarchies- in-sql – nzic

0

Bạn không thể thực hiện đệ quy trong SQL mà không có thủ tục được lưu trữ. Cách để giải quyết điều này là sử dụng Nested Sets, về cơ bản, họ mô hình hóa một cây trong SQL như một tập hợp.

Lưu ý rằng điều này sẽ yêu cầu thay đổi đối với mô hình dữ liệu hiện tại hoặc có thể tìm ra cách tạo chế độ xem trên mô hình ban đầu.

PostgreSQL dụ (sử dụng rất ít các phần mở rộng postgresql, chỉ SERIAL và ON COMMIT DROP, nhất RDBMSes sẽ có chức năng tương tự):

Setup:

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); 

Thêm một đứa trẻ:

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; 

Hiển thị đường nhỏ từ dưới lên trên:

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; 

Hiển thị toàn bộ cây:

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; 

Chọn tất cả mọi thứ xuống từ một yếu tố nào đó của cây:

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; 
+2

Nhưng bạn có thể xem câu trả lời của mathieu. –

+0

Mã tôi đã đăng sử dụng kỹ thuật áp dụng cho bất kỳ cơ sở dữ liệu ANSI-SQL nào vì OP quên đề cập đến RDBMS mà anh ta đang sử dụng trong bài đăng gốc của mình (xem nhận xét cho bài đăng đó). –

-9

Trừ Tôi thiếu điều gì đó, đệ quy không cần thiết ...

SELECT d.NAME FROM Customers As d 
INNER JOIN Customers As p ON p.CustomerID = d.ParentID 
WHERE p.Name = 'James' 
+0

bạn sẽ không nhận được Jennifer, Peter, Alice, Steve theo cách này – mathieu

3

Đối với câu trả lời dưới lên sử dụng Mathieu với một sửa đổi nhỏ:



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 


Các vấn đề liên quan