2013-12-17 14 views
1

我需要使用兩個字段來標識彼此相關的行,在某種程度上它是一個層次結構,但無法讓CTE正常工作。SQL確定相關行(Hierarchal)

這是一個簡化表:

CREATE TABLE [dbo].[LinkedRows](
    [ID] [int] NULL, 
    [COL1] [nvarchar](50) NULL, 
    [COL2] [nvarchar](50) NULL, 
    [LINK] [int] NULL 
) ON [PRIMARY] 

這裏有一些數據:

INSERT INTO dbo.LinkedRows (ID, COL1, COL2) VALUES (1, 'A','B') 
INSERT INTO dbo.LinkedRows (ID, COL1, COL2) VALUES (2, 'C','B') 
INSERT INTO dbo.LinkedRows (ID, COL1, COL2) VALUES (3, 'A','D') 
INSERT INTO dbo.LinkedRows (ID, COL1, COL2) VALUES (4, 'D','A') 
INSERT INTO dbo.LinkedRows (ID, COL1, COL2) VALUES (5, 'B','A') 
INSERT INTO dbo.LinkedRows (ID, COL1, COL2) VALUES (6, 'E','C') 
INSERT INTO dbo.LinkedRows (ID, COL1, COL2) VALUES (7, 'B','C') 
INSERT INTO dbo.LinkedRows (ID, COL1, COL2) VALUES (8, 'C','E') 
INSERT INTO dbo.LinkedRows (ID, COL1, COL2) VALUES (9, 'F','G') 
INSERT INTO dbo.LinkedRows (ID, COL1, COL2) VALUES (10, 'G','H') 

這就是我想要確定:

ID  COL1 COL2 linked 
1  A  B  1 
2  C  B  1 
3  A  D  1 
4  D  A  1 
5  B  A  1 
6  E  C  1 
7  B  C  1 
8  C  E  1 
9  F  G  2 
10  G  H  2 

行1和2兩在COL2中有B,因此A,B和C都是相互關聯的並且被賦予相同的鏈接號碼(ID)。 在第3和第4行中,我們有A,D和D,A,因爲在第5行中有B,A的組合,D可以通過A連接到B,所以現在我們可以將A,B,C,D連接在一起。 在第6和第8行中,我們有E,C和C,E,所以我們現在可以將A,B,C,D,E鏈接在一起。 第7行恰好與第2行相反。 在第9行中,我們有F,G,因爲我們無法建立從A,B,C,D,E中的任何一個到另一個鏈接組的鏈接。 在第10行中,我們有G,H,它可以通過G鏈接到鏈接組2,即F,G,H是相關的。

任何幫助或建議非常感謝。

問候 CM

回答

1

我不知道這可以用遞歸CTE在一個不錯的方式進行。

但是由於表中已經有LINK字段,因此可以使用SQL代碼填寫該字段。每次數據更改時,您都必須運行代碼:

-- Reset LINK 
UPDATE LinkedRows SET LINK = NULL; 

-- Declare variables 
DECLARE @LinkCounter int; 
DECLARE @myID int; 
DECLARE @myCOL1 nvarchar(50); 
DECLARE @myCOL2 nvarchar(50); 
DECLARE @myLINK1 int; 
DECLARE @myLINK2 int; 
-- Init group counter 
SET @LinkCounter = 0; 

DECLARE RowCursor CURSOR FOR 
SELECT ID, COL1, COL2 FROM LinkedRows ORDER BY ID; 

OPEN RowCursor; 

-- Run through all records 
FETCH NEXT FROM RowCursor INTO @myID, @myCOL1, @myCOL2; 
WHILE @@FETCH_STATUS = 0 -- More records 
    BEGIN 
     DECLARE LinkCursor CURSOR FOR SELECT DISTINCT(LINK) FROM LinkedRows WHERE (COL1 = @myCOL1 OR COL1 = @myCOL2 OR COL2 = @myCOL1 OR COL2 = @myCOL2) AND NOT LINK IS NULL; 
     OPEN LinkCursor; 
     FETCH NEXT FROM LinkCursor INTO @myLINK1; 
     IF @@FETCH_STATUS = 0 -- At least one record 
      BEGIN 
       FETCH NEXT FROM LinkCursor INTO @myLINK2; 
       IF @@FETCH_STATUS = 0 -- Two records present - Link two groups 
        BEGIN 
         -- SELECT 'Link', @myLINK1, @myLINK2; -- Debug 
         -- Join the groups - use the first LINK id 
         UPDATE LinkedRows SET LINK = @myLINK1 WHERE LINK = @myLINK2; 
         -- Add the new record to the group 
         UPDATE LinkedRows SET LINK = @myLINK1 WHERE ID = @myID; 
        END; 
       ELSE -- Only one group - assign new record to existing group 
        BEGIN 
         -- SELECT 'Assign', @myLINK1; -- Debug 
         UPDATE LinkedRows SET LINK = @myLINK1 WHERE ID = @myID; 
        END; 
      END; 
     ELSE -- New group 
      BEGIN 
       SET @LinkCounter = @LinkCounter + 1; 
       -- SELECT 'New', @LinkCounter; 
       UPDATE LinkedRows SET LINK = @LinkCounter WHERE ID = @myID; 
      END; 
     CLOSE LinkCursor 
     DEALLOCATE LinkCursor 
     -- Get next record 
     FETCH NEXT FROM RowCursor INTO @myID, @myCOL1, @myCOL2; 
    END; 
CLOSE RowCursor; 
DEALLOCATE RowCursor; 

現在LINK字段將包含鏈接組。

EDITED更新了代碼以處理兩個組將通過鏈接記錄加入的情況。

+0

非常感謝你的代碼效果很好,我的真實世界的數據可能包含不少行,所以我遠離使用遊標,你會說在這種情況下游標是不可避免的嗎?非常感謝,再次感謝。 –

+0

我認爲很難避免某種迭代,無論是在T-SQL中使用遊標還是使用一些外部代碼來運行數據。 對於遞歸CTE的工作,您將需要一個定義良好的終止條件(錨定成員),在這種情況下我看不出如何定義它。 –

+0

我的第一個答案是有缺陷的。它沒有處理兩個小組將由後來的記錄加入的情況。例如:如果有一個記錄:(ID,COL1,COL2)VALUES(11,'C','H'),則應將前兩組加入到一個組中。更新了處理這個問題的代碼。 –