在SQL select查询中排序结果

Pet*_*son 7 sql t-sql sql-server select

假设我有一个表格Events,其数据类似于以下内容:

  ID |      Name      |    ParentEvent
 ----+----------------+-----------------
   0 |   Happy Event  |       NULL
   1 |    Sad Event   |       NULL
   2 |Very Happy Event|        0
   3 | Very Sad Event |        1
   4 | Happiest Event |        2
   5 |Unpleasant Event|        1
Run Code Online (Sandbox Code Playgroud)

如何查询此表以获得以这种方式返回的结果

  • 具有非null ParentEvent的事件在事件发生后直接出现,并进行ID数学计算ParentEvent
  • 具有null ParentEvent的事件的深度为0.如果事件的深度为n,则其为父项的任何事件的深度为n + 1.
  • 只要结果满足前两个条件,结果出现的顺序无关紧要.

对于上面给出的表,我想得到一个看起来像的结果集

  ID |      Name      |  ParentEvent |  Depth |
 ----+----------------+--------------+--------+
   0 |   Happy Event  |      NULL    |    0   |  
   2 |Very Happy Event|       0      |    1   |
   4 | Happiest Event |       2      |    2   |
   1 |    Sad Event   |      NULL    |    0   |
   3 | Very Sad Event |       1      |    1   |
   5 |Unpleasant Event|       1      |    1   |
Run Code Online (Sandbox Code Playgroud)

如何构造SQL查询以获取此结果集?我正在使用T-SQL,但如果您可以在任何SQL风格中执行此操作,请继续回答.

Eri*_*ikE 8

以下查询都返回您要求的确切结果集.所有这些工作都是通过计算根节点的完整路径,并使用一些技术使该路径能够被排序.

SQL Server 2008及更高版本.在这里,通过转换为hierarchyid数据类型,SQL Server可以正确处理排序.

WITH Data AS (
   SELECT
      ID,
      Name,
      ParentID,
      Depth = 0,
      Ancestry = '/' + Convert(varchar(max), ID) + '/'
   FROM
      hierarchy
   WHERE
      ParentID IS NULL
   UNION ALL
   SELECT
      H.ID,
      H.Name,
      H.ParentID,
      D.Depth + 1,
      Ancestry = D.Ancestry + Convert(varchar(max), H.ID) + '/'
   FROM
      Data D
      INNER JOIN hierarchy H
         ON H.ParentID = D.ID
)
SELECT
   ID,
   Name,
   ParentID,
   Depth
FROM Data
ORDER BY Convert(hierarchyid, Ancestry);
Run Code Online (Sandbox Code Playgroud)

SQL Server 2005及以上版本.我们可以将ID值转换为字符串并填充它们以便排序.

WITH Data AS (
   SELECT
      ID,
      Name,
      ParentID,
      Depth = 0,
      Ancestry = Right('0000000000' + Convert(varchar(max), ID), 10)
   FROM
      hierarchy
   WHERE
      ParentID IS NULL
   UNION ALL
   SELECT
      H.ID,
      H.Name,
      H.ParentID,
      Depth + 1,
      Ancestry = D.Ancestry + Right('0000000000' + Convert(varchar(max), H.ID), 10)
   FROM
      Data D
      INNER JOIN hierarchy H
         ON H.ParentID = D.ID
)
SELECT
   ID,
   Name,
   ParentID,
   Depth
FROM Data
ORDER BY Ancestry;
Run Code Online (Sandbox Code Playgroud)

我们也可以使用varbinary(否则,这与先前的查询相同):

WITH Data AS (
   SELECT
      ID,
      Name,
      ParentID,
      Depth = 0,
      Ancestry = Convert(varbinary(max), Convert(varbinary(4), ID))
   FROM
      hierarchy
   WHERE
      ParentID IS NULL
   UNION ALL
   SELECT
      H.ID,
      H.Name,
      H.ParentID,
      Depth + 1,
      Ancestry = D.Ancestry + Convert(varbinary(4), H.ID)
   FROM
      Data D
      INNER JOIN hierarchy H
         ON H.ParentID = D.ID
)
SELECT
   ID,
   Name,
   ParentID,
   Depth
FROM Data
ORDER BY Ancestry;
Run Code Online (Sandbox Code Playgroud)

SQL Server 2000及更高版本,允许树最多深度为800级:

SELECT
   *,
   Ancestry = CASE WHEN ParentID IS NULL THEN Convert(varchar(8000), Right('0000000000' + Convert(varchar(10), ID), 10)) ELSE '' END,
   Depth = 0
INTO #hierarchy
FROM hierarchy;

WHILE @@RowCount > 0 BEGIN
   UPDATE H
   SET
      H.Ancestry = P.Ancestry + Right('0000000000' + Convert(varchar(8000), H.ID), 10),
      H.Depth = P.Depth + 1
   FROM
      #hierarchy H
      INNER JOIN #hierarchy P
         ON H.ParentID = P.ID
   WHERE
      H.Ancestry = ''
      AND P.Ancestry <> '';
END;

SELECT
   ID,
   Name,
   ParentID,
   Depth
FROM #hierarchy
ORDER BY Ancestry;

DROP TABLE #hierarchy;
Run Code Online (Sandbox Code Playgroud)

varbinary可以进行相同的转换,允许最多2000级.