2009-11-06 20 views
36

Xin chào. Tôi có một bảng tham chiếu chính nó và tôi cần để có thể chọn phụ huynh và tất cả các hồ sơ con của nó từ một ID cha cụ thể.Máy chủ SQL: Làm thế nào để có được tất cả các bản ghi con cho một id cha trong một bảng tự tham khảo

bảng của tôi là như sau:

ID | ParentID | Name   
-----------------------    
1  NULL  A 
2  1   B-1 
3  1   B-2 
4  2   C-1 
5  2   C-2 

Vì vậy, cho ví dụ trên tôi muốn để có thể vượt qua trong một giá trị là 1 và nhận được tất cả các hồ sơ trên.

Cho đến nay, tôi đã đưa ra hàm đệ quy có giá trị theo hàm nhưng nó không hoạt động như mong đợi (chỉ trả lại bản ghi đầu tiên).

CREATE FUNCTION [dbo].[SelectBranches] 
( 
    @id INT 
    ,@parentId INT 
) 
RETURNS @branchTable TABLE 
(
    ID INT 
    ,ParentID INT 
    ,Name INT 
) 
AS 
BEGIN 

    IF @branchId IS NOT NULL BEGIN 

     INSERT INTO @branchTable 
     SELECT 
      ID 
      ,ParentID 
      ,Name 
     FROM 
      tblLinkAdvertiserCity 
     WHERE 
      ID = @id 

    END 

    INSERT INTO @branchTable 
    SELECT 
     br.ID 
     ,br.ParentID 
     ,br.Name 
    FROM 
     @branchTable b 
    CROSS APPLY 
     dbo.SelectBranches(NULL, b.ParentID) br 

    RETURN 
END 
GO 
+2

+1 để cố gắng tự giải quyết, trước khi đặt câu hỏi tại đây. – iMatoria

Trả lời

52

Bạn có thể thử này

DECLARE @Table TABLE(
     ID INT, 
     ParentID INT, 
     NAME VARCHAR(20) 
) 

INSERT INTO @Table (ID,ParentID,[NAME]) SELECT 1, NULL, 'A' 
INSERT INTO @Table (ID,ParentID,[NAME]) SELECT 2, 1, 'B-1' 
INSERT INTO @Table (ID,ParentID,[NAME]) SELECT 3, 1, 'B-2' 
INSERT INTO @Table (ID,ParentID,[NAME]) SELECT 4, 2, 'C-1' 
INSERT INTO @Table (ID,ParentID,[NAME]) SELECT 5, 2, 'C-2' 


DECLARE @ID INT 

SELECT @ID = 2 

;WITH ret AS(
     SELECT * 
     FROM @Table 
     WHERE ID = @ID 
     UNION ALL 
     SELECT t.* 
     FROM @Table t INNER JOIN 
       ret r ON t.ParentID = r.ID 
) 

SELECT * 
FROM ret 
+0

+1 CTE là tuyệt vời cho đệ quy, họ có giới hạn tương đối nhỏ về số lần họ có thể gọi mình mặc dù. Nếu mức độ làm tổ của bạn thực sự sâu thì hãy cẩn thận. Tôi nghĩ giới hạn là 100. –

+2

@Robin Day: mặc định là 100, bạn có thể thay đổi bằng cách thêm "WITH MAXRECURSION number" vào cuối truy vấn. Số 0 có nghĩa là không có giới hạn. – Andomar

+1

Gợi ý MAXRECURSION có giá trị từ 0 đến 32,767 –

0

Trừ khi bạn đang sử dụng Oracle, cấu trúc bảng của bạn không phù hợp cho vấn đề mô tả. Những gì bạn đang cố gắng làm là lấy một hệ thống phân cấp (duyệt qua một cấu trúc cây).

Có một bài viết, More Trees & Hierarchies in SQL, mô tả một phương pháp giải quyết vấn đề phân cấp. Về cơ bản, anh ta thêm cột "dòng dõi" mô tả thứ bậc cho mỗi hàng.

+1

Câu hỏi cho biết anh ta đang sử dụng SQL Server 2005;) – Andomar

+1

@Abtin Thật không may là tôi đang làm việc với một hệ thống cũ nên không thể thay đổi lược đồ cơ sở dữ liệu :( –

+1

Sau đó, đệ quy FTW. –

0

Đệ quy trong CTE có vẻ hơi đắt tiền, vì vậy tôi đã viết chức năng này sử dụng chức năng gọi đệ quy nhưng nhanh hơn nhiều khi truy cập CTE.

CREATE FUNCTION [dbo].[Fn_GetSubCategories] 
(
@p_ParentCategoryId INT 
) RETURNS @ResultTable TABLE 
( 
    Id INT 
) 
AS 
BEGIN 
--Insert first level subcategories. 
INSERT INTO @ResultTable 
SELECT Id FROM Category WHERE ParentCategoryId = @p_ParentCategoryId OR Id = @p_ParentCategoryId 

DECLARE @Id INT 
DECLARE @ParentCategory TABLE(Id INT) 

DECLARE cur_categories CURSOR 
LOCAL STATIC READ_ONLY FORWARD_ONLY FOR 
SELECT Id FROM Category WHERE ParentCategoryId = @p_ParentCategoryId and Id != @p_ParentCategoryId 
OPEN cur_categories 
IF @@CURSOR_ROWS > 0 
    BEGIN 
    FETCH NEXT FROM cur_categories INTO @Id 
    WHILE @@FETCH_STATUS = 0 
    BEGIN 
     --Insert remaining level sub categories. 
     IF EXISTS(SELECT 1 FROM Category WHERE ParentCategoryId = @Id AND Id != @Id) 
     BEGIN 
      INSERT INTO @ResultTable 
      SELECT DISTINCT C.Id from Fn_GetSubCategories(@Id) C INNER JOIN @ResultTable R ON C.Id != R.Id 
     END 

    FETCH NEXT FROM cur_categories INTO @Id 
    END 

    --Delete duplicate records 
    ;WITH CTE AS 
    (SELECT *,ROW_NUMBER() OVER (PARTITION BY Id ORDER BY Id) AS RN FROM @ResultTable) 
    DELETE FROM CTE WHERE RN<>1 

END 
CLOSE cur_categories 
DEALLOCATE cur_categories 

RETURN 

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