关于tsql:SQL Server 2012多级层次查询中按一定顺序排列子项

Order child items in a certain order in a multi-level hierarchy query in SQL Server 2012

我在 SQL Server 2012 中有一个名为 Items 的表,其中包含通过 ParentId 列在父子关系中相互关联的不同项目。此表包含 ItemId 为 429965 的最顶层项目及其子、孙、曾孙等,即它包含最顶层项目的多级层次结构。

我尝试过的示例数据和查询的情况演示在此问题的 SQL Fiddle 中。 Position 的计算列告诉层次项目的显示顺序。

我正在尝试获取最顶层项目的完整层次结构,以便满足以下 requirements

  • 在此多级层次结构中,父项紧随其后的是其子项
  • 子项需要以 CreateDate 的升序显示在此层次结构中。
  • 我尝试过的查询达到了要求#1,但没有达到要求#2。

    问题

    除了要求 #1 之外,我如何使用现有的递归查询来实现要求 #2?我不能简单地按 PathStr、CreateDate 排序,因为在我的多级层次结构中没有两条路径是相同的。

    架构和示例数据创建查询

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    CREATE TABLE Items (
      ItemId int PRIMARY KEY,
      ParentId int,
      CreateDate datetime
    );


    INSERT Items
      VALUES (44129, 429965, CONVERT(datetime, '2016-01-01 17:30:55.760', 121)),
      (61291, 203905, CONVERT(datetime, '2016-01-02 20:18:35.770', 121)),
      (157898, 335625, CONVERT(datetime, '2016-01-01 00:00:06.420', 121)),
      (191951, 778472, CONVERT(datetime, '2016-01-01 00:00:01.400', 121)),
      (203905, 960767, CONVERT(datetime, '2016-01-01 00:00:01.310', 121)),
      (265468, 429965, CONVERT(datetime, '2016-05-01 06:07:26.690', 121)),
      (268246, 265468, CONVERT(datetime, '2016-10-06 13:41:55.990', 121)),
      (283015, 394157, CONVERT(datetime, '2017-12-03 01:58:08.710', 121)),
      (299356, 443367, CONVERT(datetime, '2016-01-01 00:00:01.400', 121)),
      (335625, 894441, CONVERT(datetime, '2016-11-06 21:27:00.270', 121)),
      (338413, 968392, CONVERT(datetime, '2016-11-21 07:15:48.010', 121)),
      (394157, 785375, CONVERT(datetime, '2016-05-19 09:19:28.500', 121)),
      (397189, 894441, CONVERT(datetime, '2016-01-01 13:34:03.980', 121)),
      (404536, 894441, CONVERT(datetime, '2016-01-01 00:00:16.850', 121)),
      (429965, 0, CONVERT(datetime, '2016-01-01 00:00:06.090', 121)),
      (439536, 968392, CONVERT(datetime, '2017-03-25 23:51:48.570', 121)),
      (443367, 191951, CONVERT(datetime, '2016-01-01 00:00:01.090', 121)),
      (778472, 394157, CONVERT(datetime, '2016-01-02 20:43:59.760', 121)),
      (785375, 910250, CONVERT(datetime, '2017-10-19 03:59:14.950', 121)),
      (894441, 265468, CONVERT(datetime, '2016-01-01 00:00:08.600', 121)),
      (910250, 268246, CONVERT(datetime, '2016-07-21 00:43:47.420', 121)),
      (927248, 785375, CONVERT(datetime, '2017-02-13 04:19:46.340', 121)),
      (960767, 335625, CONVERT(datetime, '2016-01-01 00:00:01.960', 121)),
      (968392, 785375, CONVERT(datetime, '2017-09-10 02:15:25.780', 121))

    我尝试过的查询

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    WITH x (ItemId, ParentId, PathStr, CreateDate)
    AS (SELECT
      ItemId,
      0 AS ParentId,
      CAST(ItemId AS varchar(max)) AS Pathstr,
      CreateDate
    FROM Items
    WHERE ItemId = 429965
    UNION ALL
    --get children for each parent ( c is for child table and x is for parent table)
    SELECT
      i.ItemId,
      i.ParentId,
      x.PathStr + '-' + CAST(i.ItemId AS varchar(max)),
      i.CreateDate
    FROM Items i
    INNER JOIN x
      ON x.ItemId = i.ParentId)
    SELECT
      *,
      ROW_NUMBER() OVER (ORDER BY PathStr) AS Position
    FROM x;

    在上述查询中,Position 为 2 和 24 的项目按 CreateDate 的降序出现,并且都是同一父项的子项目。如果排序正确,那么位置 24 的项目实际上应该有一个位置 2,位置 2 的项目应该有一个 24 的位置。


    你很接近...

    我添加了一个 Step 列,您可以使用它来查看层次结构中的级别。此外,我添加了一个 DatePosition 来检查给定 Stel 的所有日期是否按升序排列。好像没问题:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    WITH x (ItemId, ParentId, PathStr, CreateDate,Step)
    AS (SELECT
        ItemId,
        0 AS ParentId,
        CAST(ItemId AS varchar(max)) AS Pathstr,
        CreateDate,
        1 AS Step
    FROM Items
    WHERE ItemId = 429965
    UNION ALL
    --get children for each parent ( c is for child table and x is for parent table)
    SELECT
        i.ItemId,
        i.ParentId,
        x.PathStr + '-' + CAST(i.ItemId AS varchar(max)),
        i.CreateDate,
        x.Step+1
    FROM Items i
    INNER JOIN x
        ON x.ItemId = i.ParentId)
    SELECT
        *,
        ROW_NUMBER() OVER (ORDER BY PathStr) AS Position,
        ROW_NUMBER() OVER (ORDER BY CreateDate) AS DatePosition

    FROM x
    ORDER BY Step,DatePosition;

    更新:线程讨论...

    试试这个

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    WITH x (ItemId, ParentId, PathStr,CreateDate,Step)
    AS (SELECT  ItemId,
                0 AS ParentId,
                CAST(REPLACE(STR(ItemId,9),' ','0') AS VARCHAR(MAX)) AS PathStr,
                CreateDate,
                1 AS Step
        FROM Items
        WHERE ItemId = 429965

        UNION ALL
        --get children for each parent ( i is for child table and x is for parent table)
        SELECT  i.ItemId,
                i.ParentId,
                CAST(x.PathStr + '-' + REPLACE(STR(i.SortNmbr,3),' ','0') AS VARCHAR(MAX)) + '-' + CAST(REPLACE(STR(i.ItemId,9),' ','0') AS VARCHAR(MAX)),
                i.CreateDate,
                x.Step+1
        FROM x
        CROSS APPLY(SELECT *, ROW_NUMBER() OVER(ORDER BY CreateDate) AS SortNmbr FROM Items WHERE Items.ParentId=x.ItemId) AS i
       )
    SELECT *
    FROM x
    ORDER BY PathStr;

    如果你想订购一个递归 cte 的结果,你必须在你的 PathStr 中包含所有需要的东西。此外,这种排序是按字母数字完成的,因此您必须将零添加到所有数字,直到相等的宽度。

    我使用 CROSS APPLY(而不是 INNER JOIN)对相关行进行排序,并将派生的 SortNmbr(也填充了!)包含到排序字符串中。或者,可能包含日期本身,但这会导致嵌套更深的相当大的字符串。

    我的填充允许 ItemId 为 9 位,每个父母的答案计数为 3 位,这很可能。相应地减少它以减少排序字符串的大小。您可以删除连字符,它们只是为了更好地阅读。