2015-05-07 51 views
3

我已經在Microsoft SQL環境中的以下情形的父母:我們會根據孩子的ID SQL

CREATE TABLE grps 
(
    [id] varchar(50), 
    [parentid] varchar(50), 
    [value] varchar(50) 
); 

INSERT INTO grps 
    ([id], [parentid], [value]) 
VALUES 
    ('-5001', '0', null), 
    ('-5002', '-5001', null), 
    ('-5003', '-5002', '50'), 
    ('-5004', '-5003', null), 
    ('-5005', '0', null), 
    ('-5006', '0', null), 
    ('-5007', '0', null), 
    ('-5008', '-5006', null); 

我試圖讓基於孩子的父母的ID。如果被查詢的ID是最後一個父母,那麼它應該只返回最後一個項目。

例子:

  • 如果我查詢:id = '-5004'它應該返回('-5004', '-5003', null), ('-5003', '-5002', '50'), ('-5002', '-5001', null), ('-5001', '0', null)
  • 如果我查詢id = '-5007'它應該返回('-5007', '0', null)

這將是真棒,如果它可以列出ID查詢第一其餘的則以有序的方式上升到「樹」上。

我已經嘗試了幾種不同的方法與CTE的,但不幸的是沒有運氣。所以我在這裏尋找一些幫助或想法。

在此先感謝。

回答

3

你在CTE的正確軌道上。它可以通過使用遞歸CTE來完成!下面是遞歸的CTE的樣子:

DECLARE @ID varchar(50) = '5004'; 

WITH CTE AS 
(
    --This is called once to get the minimum and maximum values 
    SELECT id, parentid, value 
    FROM grps 
    WHERE id= @ID 
    UNION ALL 
    --This is called multiple times until the condition is met 
    SELECT g.id, g.parentid, g.value 
    FROM CTE c, grps g 
    WHERE g.id= c.parentid 
    --If you don't like commas between tables then you can replace the 2nd select 
    --statement with this: 
    --SELECT g.id, g.parentid, g.value 
    --FROM CTE c 
    --INNER JOIN grps g ON g.id= c.parentid 
    --This can also be written with CROSS JOINS! 
    --Even though it looks more like another way of writing INNER JOINs. 
    --SELECT g.id, g.parentid, g.value 
    --FROM CTE c 
    --CROSS JOIN grps g 
    --WHERE g.id = c.parentid 
) 

SELECT * FROM CTE 

當心最大遞歸是100,除非你加option (maxrecursion 0)到最後一個select語句的結束。 0表示無限,但您也可以將其設置爲您想要的任何值。

享受!

+0

謝謝!這正是我正在尋找的。當我得到代表時,我會鼓勵你。 – Gurby

+0

@Gurby歡迎您! :) –

2

我正在盡我所能地給予hierarchyid在世界上的一些愛。首先,設置:

CREATE TABLE grps 
(
    [id] varchar(50), 
    [parentid] varchar(50), 
    [value] varchar(50), 
    h HIERARCHYID NULL 
); 

SELECT * FROM grps 
INSERT INTO grps 
    ([id], [parentid], [value]) 
VALUES 
    ('-5001', '0', null), 
    ('-5002', '-5001', null), 
    ('-5003', '-5002', '50'), 
    ('-5004', '-5003', null), 
    ('-5005', '0', null), 
    ('-5006', '0', null), 
    ('-5007', '0', null), 
    ('-5008', '-5006', null); 

WITH cte AS (
    SELECT id , 
      parentid , 
      value , 
      CAST('/' + id + '/' AS nvarchar(max)) AS h 
    FROM grps 
    WHERE parentid = 0 

    UNION ALL 

    SELECT child.id , 
      child.parentid , 
      child.value , 
      CAST(parent.h + child.id + '/' AS NVARCHAR(MAX)) AS h 
    FROM cte AS [parent] 
    JOIN grps AS [child] 
     ON child.parentid = parent.id 
) 
UPDATE g 
SET h = c.h 
FROM grps AS g 
JOIN cte AS c 
    ON c.id = g.id 

我在這裏所做的是在您的表定義中添加一個hierarchyid列並計算它的值。要確定回答你原來的問題,現在看起來是這樣的:

SELECT g.id , 
     g.parentid , 
     g.value , 
     g.h.ToString() 
FROM dbo.grps AS g 
JOIN grps AS c 
    ON c.h.IsDescendantOf(g.h) = 1 
WHERE c.id = '-5004' 

爲了使這個更好的性能,你應該索引的ID,並獨立地爲H列(即,在單獨的索引)兩種。

此外,一對夫婦的音符

  • 具有ID列是VARCHAR當數據看起來數字是腥充其量,但更重要的是它的效率不高。如果是我,我會用int。但是,也許你的實際數據更加混亂(即你有像'A1234'這樣的ID)。

  • 我還會用NULL而不是0代表頂級(即沒有父級的)成員。但是這更多的是一種個人選擇,而不是對性能有實質影響的選擇。

+0

有一個hierarchyID有什麼好處? –

+1

優秀的問題。因爲您要將行列存儲起來,所以您不必在運行時計算它。所以,如果你的工作量很大,這可以爲你節省很多時間,因爲你只需要計算一次層次結構。 –

+1

此外,使用遞歸cte方式,您每觸摸一次每一行。有了這個,你應該只能找到相關的行。 –