FOREACH递归SQL语句

时间:2011-10-22 21:12:06

标签: c# sql tsql sql-server-2008

我有一个看似简单的问题,但我迄今为止尝试的解决方案让我想要执行区域。在小(<10000)数据集上速度似乎很好但随着计数的增加很快就会花费越来越多的时间。

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

填写Id和ParentId信息.Id始终具有值,如果行没有父项,则ParentId为空,否则表示父行中的Id的值。

问题是Id和ParentId到处都是。将所有ID添加到表中,然后处理它们以添加子项。这是问题的一部分,不是可以改变的。

我需要做的是生成ControlNum值,以便遵守Parent Child关系。我当前的逻辑使用了一些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);

Baseline正试图让这个执行更快,理想情况下完全在SQL中会被优先考虑。我正在尝试使用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

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个。具有父项的对象位于底部,这些是根条目的示例,其控制编号和父控制编号设置为相同值。在表的顶部,我们看到一些对象具有相同的父对象,因此具有匹配的父控件号和相当接近的控件号(例如,ControlNum 22和24之间的行也必须来自父21)。到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,然后下一个对象需要是它的第一个子对象,然后是对象子等等,然后再继续到下一个根对象。

我想我所说的是这是一个广度优先,我需要的是深度优先。

1 个答案:

答案 0 :(得分: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-Data上运行它,稍加修改输入: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