2014-07-22 33 views
7

Giả sử các gia đình dưới đây:Nhận các thành viên gia đình

Family Tree

Schema xây dựng của việc này là:

create table PersonConn (child int, parent int) 
insert into PersonConn values (1,2) 
insert into PersonConn values (1,3) 
insert into PersonConn values (5,3) 
insert into PersonConn values (5,4) 
insert into PersonConn values (6,7) 
insert into PersonConn values (6,8) 
insert into PersonConn values (2,9) 
insert into PersonConn values (2,10) 
insert into PersonConn values (3,11) 
insert into PersonConn values (3,12) 

Để có được tổ tiên của một thành viên trong gia đình tôi có thể sử dụng đệ quy như cho thấy dưới đây:

WITH Childs AS (
    SELECT distinct Child, Parent 
    FROM PersonConn 
    WHERE Child = 1 
    UNION ALL 
    SELECT t2.Child, t2.Parent 
    FROM [Childs] t1 
    INNER JOIN PersonConn t2 
     ON t2.Child = t1.parent 
) 
SELECT PARENT FROM CHILDS 

SQL Fiddle

Nó sẽ lấy tất cả tổ tiên của thành viên được chọn (ID 1 trong ví dụ này), nhưng không phải là anh em chẳng hạn. Truy vấn chỉ tăng lên trong cây gia đình.

My câu hỏi là:

Làm thế nào để có được tất cả các thành viên của một gia đình (con trai, cha mẹ, ông nội, chú bác, anh em họ, vv ...) bắt đầu từ một người duy nhất?

CẬP NHẬT

Một phương pháp để giải quyết này là một vòng lặp mà chèn một người trong một bảng tạm thời. Sau khi bạn có thể tham gia PersonConn bảng với bảng tạm thời này và chèn người khác. Làm điều này cho đến khi không có ai được chèn vào nữa. Tôi đang tìm kiếm một cách hiệu quả hơn (và thanh lịch). Tôi có khoảng 200MM bản ghi trong bảng PersonConn.

+0

Bạn sẽ cần chèn gốc vào PersonConn ... Thay vì chọn nơi CHILD = 1, bạn sẽ làm nơi PARENT IS NULL. Điều đó sẽ cung cấp cho bạn tất cả các thành viên, bắt đầu từ gốc xuống dưới. – Mez

+0

Thực ra tôi muốn chọn một ID người (con hoặc cha mẹ) và nhận tất cả các thành viên gia đình kết nối với người đó. Tôi không có một người gốc duy nhất với các thành viên dưới đây. Tôi có một nhóm người kết nối với tư cách là cha mẹ và con cái. Quan sát ví dụ đã cho tôi có 6 người không có cha mẹ thông báo. – Nizam

+0

Bạn có thể chỉ định cấu trúc cây của gia đình - và bạn có 2 lá có 1 cha mẹ. Tuy nhiên làm thế nào các lá liên quan đến nhau - bạn sẽ phải xác định mối quan hệ trong một cột khác. Bạn có thể cho rằng họ là anh chị em bởi vì họ ở cùng cấp độ - bạn có thể tính toán chiều sâu trong CTE, tuy nhiên tôi sẽ đề nghị trong trường hợp của bạn để xác định mối quan hệ. – Mez

Trả lời

1

Giải pháp tôi thấy không tốt chút nào. Nó đưa ra câu trả lời đúng nhưng rất chậm, ngay cả đối với cái bàn rất nhỏ này.

DECLARE @INCLUIDOS TABLE (ID INT) 

INSERT INTO @INCLUIDOS VALUES(1) 

DECLARE @PAST_QUANT INT = 0 
DECLARE @QUANT INT = 1 

WHILE @QUANT <> @PAST_QUANT 
BEGIN 

    SET @PAST_QUANT = @QUANT 

    INSERT INTO @INCLUIDOS 
     SELECT PARENT 
     FROM PERSONCONN 
     WHERE CHILD IN (SELECT ID FROM @INCLUIDOS) 
      AND PARENT NOT IN (SELECT ID FROM @INCLUIDOS) 

    INSERT INTO @INCLUIDOS 
     SELECT CHILD 
     FROM PERSONCONN 
     WHERE PARENT IN (SELECT ID FROM @INCLUIDOS) 
      AND CHILD NOT IN (SELECT ID FROM @INCLUIDOS) 

    SET @QUANT = (SELECT COUNT(*) FROM @INCLUIDOS) 

END 

SELECT DISTINCT ID FROM @INCLUIDOS 

SQL Fiddle

+0

Tác phẩm này hoạt động nhưng chậm. Sau đó, nó là một điểm chuẩn tốt để kiểm tra các giải pháp khác ở đây. – Nizam

1

Trong đầu tôi đề nghị bạn rằng cột sử dụng hierarchyid cho bảng của bạn.

Hãy thử sau đây truy vấn (không HierarchyId):

DECLARE @PersonId INT = 3 

;WITH Parents AS (
    SELECT @PersonId AS Id 
    UNION ALL 
    SELECT child 
    FROM PersonConn pc 
    INNER JOIN Parents p ON pc.parent = p.Id 
    ), 
    Childs AS (
    SELECT distinct pc.Child, pc.Parent 
    FROM PersonConn pc 
    INNER JOIN Parents p ON pc.child = p.Id OR pc.parent = p.Id 
    UNION ALL 
    SELECT t2.Child, t2.Parent 
    FROM [Childs] t1 
    INNER JOIN PersonConn t2 
     ON t2.Child = t1.parent 
) 
SELECT DISTINCT CASE WHEN N.n=1 THEN parent ELSE child END 
FROM CHILDS 
CROSS APPLY(SELECT 1 UNION SELECT 2)N(n) 

SQL Fiddle

+0

Rực rỡ. Trước khi chấp nhận nó, tôi sẽ thử dữ liệu thực của tôi. Đó là một giải pháp rất tốt. Xin chúc mừng!!! – Nizam

+0

Truy vấn rất đẹp nhưng nó không hoạt động. Phải mất tất cả các hậu duệ trực tiếp và sau đó tất cả tổ tiên của những người này. Nhưng, nó không xem xét hậu duệ của những tổ tiên mới này. Bằng cách này, nếu bạn bắt đầu từ @PersonID = 10, nó sẽ mất 10,2,1 với Cha mẹ, thêm 9,3,11,12 với Childs, nhưng sẽ không xem xét 4 và 5. Vấn đề này thực sự khó khăn. Đó là lý do tại sao tôi sẽ không chấp nhận câu trả lời. – Nizam

+0

Thực ra, nó chỉ hoạt động với @PersonId bằng 3. Chính xác nguồn gốc bạn đã chọn (trùng hợp ngẫu nhiên?). – Nizam

0

này nên làm việc cho bất kỳ nút. Công việc cốt lõi của CTE chỉ khi bạn bắt đầu với nút con gốc. Vì vậy, phần đầu tiên tìm thấy một đứa trẻ gốc nếu người bắt đầu không phải là một. Kỹ thuật này là để đi lên các hệ thống phân cấp, sau đó xuống, sau đó sao lưu để có được tất cả mọi người trong một gia đình.

DECLARE @PersonId INT = 10 

-- if id passed in is not a root child, then get one 
If (SELECT Top 1 Parent FROM PersonConn WHERE Child = @PersonId) is null 
    WITH CHILDS AS (
     SELECT Child, 0 as [level] 
     FROM PersonConn 
     WHERE Parent = @PersonId 
     UNION ALL 
     SELECT t2.Child, [level] + 1 
     FROM CHILDS t1 
     INNER JOIN PersonConn t2 
     ON t2.Parent = t1.Child 
    ) 
    SELECT Top 1 @PersonId = Child FROM CHILDS ORDER BY [level] Desc; 

WITH CHILDS AS (
    SELECT Child, Parent 
    FROM PersonConn 
    WHERE Child = @PersonId 
    UNION ALL 
    SELECT t2.Child, t2.Parent 
    FROM CHILDS t1 
    INNER JOIN PersonConn t2 
    ON t2.Child = t1.parent 
    ), 
PARENTS AS (
    SELECT Child, Parent 
    FROM PersonConn 
    WHERE Parent in (Select parent from CHILDS) 
    UNION ALL 
    SELECT t2.Child, t2.Parent 
    FROM PARENTS t1 
    INNER JOIN PersonConn t2 
    ON t2.parent = t1.child 
    ), 
CHILDS2 AS (
    SELECT Child, Parent 
    FROM PersonConn 
    WHERE Child in(Select child From Parents) 
    UNION ALL 
    SELECT t2.Child, t2.Parent 
    FROM CHILDS2 t1 
    INNER JOIN PersonConn t2 
    ON t2.Child = t1.parent 
) 
SELECT DISTINCT Parent, Child FROM CHILDS2 

Tôi chắc chắn không nghĩ điều này là thanh lịch và tôi không thể tưởng tượng nó sẽ hoạt động tốt. Tôi sẽ quan tâm đến việc biết nó hoạt động tốt như thế nào trên khối lượng dữ liệu của bạn. Bạn không chắc chắn chính xác cách bạn sẽ sử dụng nó trong sản xuất, nhưng tôi khuyên bạn nên tạo một trường khác và điền vào nó để xác định toàn bộ gia đình nếu đây là điều bạn phải làm thường xuyên.

+0

Tôi rất muốn có thể làm điều đó, nhưng tôi không biết trước một gia đình của những người, không phải những người chèn dữ liệu. Quá trình chèn là ** VERY ** được phân phối. Tôi sẽ kiểm tra truy vấn của bạn và sẽ phản hồi sau. Cảm ơn rất nhiều. – Nizam

+0

Vấn đề của giải pháp này, như ở trên, là bạn đang xem xét việc đi xuống và lên một vài lần sẽ giải quyết được vấn đề. Tôi luôn có thể xây dựng một gia đình mà thuật toán này sẽ không nắm bắt được nó hoàn toàn. Tất nhiên một thuật toán như của bạn sẽ làm việc 90% số lần, nhưng nó vẫn sẽ thất bại 10%. – Nizam

+0

Tôi sẽ thử và quay lại câu trả lời ngày 23 tháng 7 của bạn. Tôi đơn giản hóa trong một câu trả lời khác. Thực hiện tốt cho tôi với một chiếc bàn nhỏ. – Luke

0

Đây là phiên bản đơn giản của câu trả lời ngày 23 tháng 7 của Nizam. Với bảng PersonConn được lập chỉ mục đúng cách, tôi nhận được kết quả rất tốt (tuy nhiên tôi không có cách nào để kiểm tra nó với 200 triệu bản ghi).Nếu một bảng tạm thời được sử dụng thay vì một biến bảng, bạn có thể chỉ mục ID, nhưng tôi không nghĩ rằng điều này sẽ cải thiện hiệu suất nhiều vì chỉ mục sẽ cần cập nhật sau mỗi lần chèn.

DECLARE @INCLUIDOS TABLE (ID INT) 
INSERT INTO @INCLUIDOS VALUES(5) 

WHILE @@ROWCOUNT <> 0 
BEGIN 

INSERT INTO @INCLUIDOS 
    SELECT CHILD 
    FROM PERSONCONN 
    WHERE PARENT IN (SELECT ID FROM @INCLUIDOS) 
     AND CHILD NOT IN (SELECT ID FROM @INCLUIDOS) 

INSERT INTO @INCLUIDOS 
    SELECT PARENT 
    FROM PERSONCONN 
    WHERE CHILD IN (SELECT ID FROM @INCLUIDOS) 
     AND PARENT NOT IN (SELECT ID FROM @INCLUIDOS) 
END 

SELECT ID FROM @INCLUIDOS 
Các vấn đề liên quan