2010-07-01 7 views
6

Tôi đang cố gắng thực hiện những gì tôi tin là một đệ quy khó khăn bằng cách sử dụng một CTE là SQL Server 2008. Tôi không thể có vẻ quấn quanh đầu của tôi.SQL Server 2008 CTE đệ quy

Trong các ví dụ bên dưới, bạn có thể giả định độ sâu cố định là 3 ... không có gì sẽ thấp hơn mức đó. Trong cuộc sống thực, độ sâu là "sâu hơn" nhưng vẫn cố định. Trong ví dụ này, tôi đã cố gắng đơn giản hóa nó một số.

Dữ liệu đầu vào của tôi giống như dưới đây.

ID  PARENT_ID  NAME   DEPTH 
------------------------------------------ 
1  NULL   A    1 
2  1    B    2 
3  2    C    3 
4  1    D    2 

Đầu ra của CTE của tôi phải là bảng sau đây.

LEVEL1_ID LEVEL2_ID LEVEL3_ID LEVEL1_NAME LEVEL2_NAME LEVEL3_NAME 
-------------------------------------------------------------------------------- 
1   NULL   NULL   A    NULL   NULL 
1   2   NULL   A    B    NULL 
1   2   3   A    B    C 
1   4   NULL   A    D    NULL 

Nếu tôi có thể lấy cột ID ở đầu ra, tôi có thể ánh xạ tới tên trong bảng tra cứu.

Tôi cũng đang mở các cách khác để hoàn thành việc này, bao gồm sử dụng SSIS.

+0

Tôi không thể hiểu những gì logic bạn đang áp dụng bằng cách nhìn vào đầu ra của bạn. Tôi nghĩ dòng cuối cùng phải là '/ B/D' vì id mẹ của' D' là 2 nghĩa là 'B' – IsmailS

+0

vừa cập nhật câu hỏi. lấy làm tiếc. – thomas

+0

Một lần nữa. Trong hàng # 4, Tại sao bạn cần Level1_ID là '1' khi id cha của nó là 2? – IsmailS

Trả lời

9

Không thực sự tất cả những gì khó có thể làm:

;WITH cte AS 
(
    SELECT CAST('/' + Name AS VARCHAR(50)) as 'CteName', ID 
    FROM dbo.YourTable 
    WHERE parent_id IS NULL 

    UNION ALL 

    SELECT CAST(cte.CteName + '/' + Name AS VARCHAR(50)), t.ID 
    FROM dbo.YourTable t 
    INNER JOIN cte ON t.parent_id = cte.id 
) 
SELECT cteName FROM cte 
ORDER BY ID 

Cung cấp cho tôi một sản lượng:

/A 
/A/B 
/A/B/C 
/A/D 

Là một phụ lưu ý: "chiều sâu" có thể dễ dàng tính toán bởi CTE và bạn không nhất thiết phải lưu trữ nội dung đó trong bảng của mình (xem cột Level tôi đã thêm):

;WITH cte AS 
(
    SELECT 
     CAST('/' + Name AS VARCHAR(50)) as 'CteName', ID, 
     1 AS 'Level' 
    FROM dbo.YourTable 
    WHERE parent_id IS NULL 

    UNION ALL 

    SELECT 
     CAST(cte.CteName + '/' + Name AS VARCHAR(50)), t.ID, 
     cte.Level + 1 AS 'Level' 
    FROM dbo.YourTable t 
    INNER JOIN cte ON t.parent_id = cte.id 
) 
SELECT cteName FROM cte 
ORDER BY Level, ID 
+1

điều này thật tuyệt vời. cảm ơn! Tôi đã thay đổi câu hỏi một chút nhưng điều này là tuyệt vời. Tôi chắc chắn có thể sử dụng ví dụ này. – thomas

+0

Great marc_s! Nhanh quá !!!! – IsmailS

+0

Với mã này bạn không có vấn đề tham gia các cấp nhiều lần? Ví dụ trong lần chạy đầu tiên của chạy đệ quy, bạn sẽ tham gia cấp 1 với cấp 2, trong lần chạy thứ 2, bạn sẽ tham gia lại cấp 1 với cấp 2 và cấp 2 với cấp 3, v.v. được "gỡ bỏ" b toán tử UNION, nhưng nó sẽ thực hiện rất nhiều phép nhân đôi. – munissor

0

Tôi không nhớ bạn có thể làm một truy vấn phụ trong một cte.

Tôi không có một bản sao của máy chủ sql đây nhưng bạn có thể thử với mã này:

WITH cte(id, path, level) 
AS 
(
    SELECT id, '/' + name, level 
    FROM yourtable 
    WHERE level = 1 

    UNION ALL 

    SELECT y.id, c.name + '/' + y.name, y.level 
    FROM yourtable y INNER JOIN 
    cte c ON c.id = y.parent_id 
    WHERE level = (SELECT max(level)+1 from cte) 
) 
SELECT path from cte 
+0

Tôi chỉ nhận được một tấn lỗi: Msg 207, Cấp 16, Tiểu bang 1, Dòng 6 Tên cột không hợp lệ 'cấp'. Msg 207, Cấp 16, Tiểu bang 1, Dòng 4 Tên cột không hợp lệ 'cấp'. Msg 253, Cấp 16, Tiểu bang 1, Dòng 1 Thành viên đệ quy của một biểu thức bảng chung 'cte' có nhiều tham chiếu đệ quy. –

+1

Ok nên bạn không thể sử dụng truy vấn con;) – munissor

-1
;WITH Vals AS (
     SELECT CASE DEPTH WHEN 1 THEN ID ELSE NULL END 'LEVEL1_ID ', 
       CASE DEPTH WHEN 2 THEN ID ELSE NULL END 'LEVEL2_ID ', 
       CASE DEPTH WHEN 3 THEN ID ELSE NULL END 'LEVEL3_ID ', 
       CASE DEPTH WHEN 1 THEN NAME ELSE NULL END 'LEVEL1_NAME', 
       CASE DEPTH WHEN 2 THEN NAME ELSE NULL END 'LEVEL2_NAME', 
       CASE DEPTH WHEN 3 THEN NAME ELSE NULL END 'LEVEL3_NAME', 
       ID 'PRMID'     
     FROM #Table1 
     WHERE parentId IS NULL 
     UNION ALL 
     SELECT CASE DEPTH WHEN 1 THEN ID ELSE LEVEL1_ID END 'LEVEL1_ID ', 
       CASE DEPTH WHEN 2 THEN ID ELSE LEVEL2_ID END 'LEVEL2_ID ', 
       CASE DEPTH WHEN 3 THEN ID ELSE LEVEL3_ID END 'LEVEL3_ID ', 
       CASE DEPTH WHEN 1 THEN NAME ELSE LEVEL1_NAME END 'LEVEL1_NAME', 
       CASE DEPTH WHEN 2 THEN NAME ELSE LEVEL2_NAME END 'LEVEL2_NAME', 
       CASE DEPTH WHEN 3 THEN NAME ELSE LEVEL3_NAME END 'LEVEL3_NAME', 
       ID 'PRMID'        
     FROM #Table1 inner join Vals on #Table1.parentId=PRMID 

) 

SELECT * from Vals