2011-10-22 78 views
2

我有什麼似乎是一個簡單的問題,但我已經嘗試過的解決方案讓我想要在執行區域。小數據集(< 10000)的速度似乎很好,但隨着數量的增加,速度會越來越快。FOREACH遞歸SQL語句

在SQL Server 2008 R2中,我有一個包含四列的表:Id,ParentId,ControlNum,ParentControlNum。

Id和ParentId信息被填充。Id始終有一個值,如果該行沒有父項,則ParentId爲null,否則它爲表示父行的表中Id的值。

問題是,Id和ParentIds都在這個地方。所有的ID都被添加到表中,然後處理它們以添加子項。這是問題的一部分,不是可以改變的。

我需要能夠做的是生成ControlNum值,以順從父子關係。我當前的邏輯使用了一點C#和SQL SELECT/UPDATE命令來實現這一點,但正如前面提到的,性能是一個非常值得關注的問題。

僞代碼

Select all Id's where the parent Id is null (All root entries) 
Foreach (Id) 
    GenerateControlNum(Id, CurrentCounterValue, CurrentCounterValue) 

GenerateControlNum(Id, CurrentCounterValue, ParentCounterValue) 
    Set Id's ControlNum to CurrentCounterValue 
    Set Id's ParentControlNum to CurrentCounterValue 

    Increment CurrentCounterValue 

    Select All Id's where ParentId == Id (All my direct children) 
    Foreach (ChildId) 
     GenerateControlNum(ChildId, CurrentCounterValue, Id's ControlNum); 

基線正在努力使這個在SQL執行得更快,idealy完全將較受歡迎。我正在嘗試使用RootIds填充的CTE的行,然後用MERGE語句通過它們,但我似乎無法使計數器值正確工作來設置ControlNum值。

這甚至可能在SQL中,或者這是一個過程類型的處理過多嗎?

示例表從它當前的運行方式的數據:BEFORE

ID          ParentId        ControlNum ParentControlNum 
8C821027-A6F9-E011-AB48-B499BAE13A62 756F981E-A6F9-E011-AB48-B499BAE13A62 0   NULL 
D7DB6033-A6F9-E011-AB48-B499BAE13A62 756F981E-A6F9-E011-AB48-B499BAE13A62 0   NULL 
D2E36033-A6F9-E011-AB48-B499BAE13A62 C9E36033-A6F9-E011-AB48-B499BAE13A62 0   NULL 
8FE66033-A6F9-E011-AB48-B499BAE13A62 58E66033-A6F9-E011-AB48-B499BAE13A62 0   NULL 
37EC6033-A6F9-E011-AB48-B499BAE13A62 2FEC6033-A6F9-E011-AB48-B499BAE13A62 0   NULL 
41EC6033-A6F9-E011-AB48-B499BAE13A62 2FEC6033-A6F9-E011-AB48-B499BAE13A62 0   NULL 
DDED6033-A6F9-E011-AB48-B499BAE13A62 BCED6033-A6F9-E011-AB48-B499BAE13A62 0   NULL 
DC69981E-A6F9-E011-AB48-B499BAE13A62 NULL         0   NULL 
166A981E-A6F9-E011-AB48-B499BAE13A62 NULL         0   NULL 
4D6A981E-A6F9-E011-AB48-B499BAE13A62 NULL         0   NULL 
856A981E-A6F9-E011-AB48-B499BAE13A62 NULL         0   NULL 
F56A981E-A6F9-E011-AB48-B499BAE13A62 NULL         0   NULL 
2E6B981E-A6F9-E011-AB48-B499BAE13A62 NULL         0   NULL 
666B981E-A6F9-E011-AB48-B499BAE13A62 NULL         0   NULL 
9D6B981E-A6F9-E011-AB48-B499BAE13A62 NULL         0   NULL 

AFTER

ID          ParentId        ControlNum ParentControlNum 
8C821027-A6F9-E011-AB48-B499BAE13A62 756F981E-A6F9-E011-AB48-B499BAE13A62 22   21 
D7DB6033-A6F9-E011-AB48-B499BAE13A62 756F981E-A6F9-E011-AB48-B499BAE13A62 24   21 
D2E36033-A6F9-E011-AB48-B499BAE13A62 C9E36033-A6F9-E011-AB48-B499BAE13A62 58   57 
8FE66033-A6F9-E011-AB48-B499BAE13A62 58E66033-A6F9-E011-AB48-B499BAE13A62 69   68 
37EC6033-A6F9-E011-AB48-B499BAE13A62 2FEC6033-A6F9-E011-AB48-B499BAE13A62 86   85 
41EC6033-A6F9-E011-AB48-B499BAE13A62 2FEC6033-A6F9-E011-AB48-B499BAE13A62 88   85 
DDED6033-A6F9-E011-AB48-B499BAE13A62 BCED6033-A6F9-E011-AB48-B499BAE13A62 95   94 
DC69981E-A6F9-E011-AB48-B499BAE13A62 NULL         0   0 
166A981E-A6F9-E011-AB48-B499BAE13A62 NULL         1   1 
4D6A981E-A6F9-E011-AB48-B499BAE13A62 NULL         2   2 
856A981E-A6F9-E011-AB48-B499BAE13A62 NULL         3   3 
F56A981E-A6F9-E011-AB48-B499BAE13A62 NULL         4   4 
2E6B981E-A6F9-E011-AB48-B499BAE13A62 NULL         5   5 
666B981E-A6F9-E011-AB48-B499BAE13A62 NULL         6   6 
9D6B981E-A6F9-E011-AB48-B499BAE13A62 NULL         7   7 

該數據集我已經是104項現在所以這只是第一15.使用對象out父母位於底部,這些都是根條目的例子,並將其控制號和父控制號設置爲相同的值。在表的頂部,我們看到一些具有相同父項的對象,因此它們具有匹配的父控制號和相當接近的控制號(例如,在ControlNum 22和24之間也必須存在來自父母21的行,例如,對於86到88跳,他們只是不在旁邊的桌子上)。

希望這個更清楚。

編輯:基於通過了Mikael

下面給出的答案更清晰是基於其ID和的ParentId信息層次顯示ControlNum值。通常情況下,這些將被列爲1,2,3 ... 8,但它更容易不會在整個消息的(孩子)消除顯示。

1 
    4 
    7 
    8 
    5 
2 
    6 
3 

我需要的是

1 
    2 
    3 
    4 
    5 
6 
    7 
8 

這就是爲什麼遞歸一直是我一直在走的方向是,我需要一個ControlNum分配給根對象,然後下一個對象需要成爲它的第一個孩子後跟着那個對象的孩子等,然後再進入下一個根對象。

我想我所說的是這是一個寬度第一,我需要的是深度第一。

+0

您可以添加一些示例數據嗎?更新之前的樣子以及更新之後的樣子。我不清楚你的意思是「爲了遵守父母子女關係而生成ControlNum值」 –

+0

@MikaelEriksson示例數據,並添加了一些解釋。 – James

回答

3

不知道我得到你的所有要求,但這裏是一個開始。告訴我,如果它做你想要的或如果數字不正確生成。

;with C as 
(
    select ID, 
     ParentID, 
     ControlNum, 
     ParentControlNum, 
     row_number() over(order by ParentID, ID) - 1 as rn 
    from YourTable 
) 
update C1 
set ControlNum = C1.rn, 
    ParentControlNum = case when C1.ParentID is null 
         then C1.rn 
         else C2.rn 
         end 
from C as C1 
    left outer join C as C2 
    on C1.ParentID = C2.ID 

具有稍微修改的輸入執行命令它在SE-數據:http://data.stackexchange.com/stackoverflow/q/115625/

2版

首先遞歸CTE R,即建立一個字符串,以通過產生的值時,可以用作順序爲ControlNum。之後它幾乎和上面一樣。

;with R as 
(
    select ID, 
     ParentID, 
     cast(ID as varchar(max)) as Sort 
    from YourTable 
    where ParentID is null 
    union all 
    select T.ID, 
     T.ParentID, 
     R.Sort+cast(T.ID as varchar(max)) 
    from YourTable as T 
    inner join R 
     on R.ID = T.ParentID 
), 
C as 
(
    select ID, 
     ParentID, 
     row_number() over(order by Sort) - 1 as rn 
    from R 
) 
update T 
set ControlNum = C1.rn, 
    ParentControlNum = case when C1.ParentID is null 
         then C1.rn 
         else C2.rn 
         end 
from YourTable as T 
    inner join C as C1 
    on T.ID = C1.ID 
    left outer join C as C2 
    on T.ParentID = C2.ID 

測試在這裏:http://data.stackexchange.com/stackoverflow/q/115626/

注:我想這是一個一次性的東西,你會帶一些數據做,因爲你將有一個很難增加新的節點,並在同一時間堅持像編號這個。例如,如果您將一個新的子節點添加到第一個節點,則必須爲所有節點「下方」分配所有ControlNum += 1,並重新分配所有ParentControlNum

+0

這是朝着正確方向邁出的一大步。然而,這並不是完全解決我的問題,我已經把我的問題擴大了。 – James

+0

在處理之前,這確實是一次數據的最終排序。這確實有用,但速度稍慢,大概是因爲字符串比較。我正在思考從第一個版本獲取數據的可能性,通過ParentControlNum對其進行排序,然後對結果使用row_number()。任何對未來進展都只是想法的人。再次感謝您的工作答案。 – James