在SQL select查询中排序结果

时间:2014-01-10 22:01:05

标签: sql sql-server tsql 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

如何查询此表以获得以

方式返回的结果
  • 具有非空ParentEvent的事件会在事件发生后直接显示ID数学ParentEvent
  • 具有空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   |

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

3 个答案:

答案 0 :(得分: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);

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;

我们也可以使用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;

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;

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

答案 1 :(得分:1)

这只是增加M.Ali的答案。我意识到OP说,“只要结果满足前两个条件,结果出现的顺序无关紧要”。但是,通过向查询中添加一个跟踪层次结构路径的列,可以显示与问题中相同的结果。

;WITH CTE
AS
(
  SELECT 
    ID, 
    NAME,
    ParentID,
    0 as Depth,
    convert(varbinary(max), convert(varbinary(2), ID)) as ThePath
  FROM   hierarchy
  WHERE ParentID is null

  UNION ALL

  SELECT 
    h.ID, 
    h.NAME,
    h.ParentID, 
    cte.Depth + 1,
    cte.ThePath + convert(varbinary(max), convert(varbinary(2), h.ID)) as ThePath
  FROM   hierarchy AS h 
  INNER JOIN CTE as cte
  ON h.ParentID = cte.ID
)
SELECT 
  ID, 
  NAME, 
  ParentID, 
  Depth, 
  ThePath
FROM CTE 
ORDER BY ThePath

这会显示如下结果。

ID          NAME                           ParentID    Depth       ThePath
----------- ------------------------------ ----------- ----------- ---------------
0           Happy Event                    NULL        0           0x0000
2           Very Happy Event               0           1           0x00000002
4           Happiest Event                 2           2           0x000000020004
1           Sad Event                      NULL        0           0x0001
3           Very Sad Event                 1           1           0x00010003
5           Unpleasant Event               1           1           0x00010005

答案 2 :(得分:-2)

测试数据

CREATE TABLE hierarchy (ID INT, NAME NVARCHAR(30), ParentID INT)
INSERT INTO hierarchy VALUES 
(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)

查询(Sql Server 2005 +)

;WITH ClassHierarchy_CTE (CID, ClassID_Join, Level)
AS
(
SELECT ID, ID AS Join_Class, 0
FROM   hierarchy AS c

UNION ALL
SELECT cte.CID, h.ParentID, Level + 1
FROM   hierarchy AS h INNER JOIN ClassHierarchy_CTE as cte
ON     h.ID = cte.ClassID_Join
)
SELECT    CTE.CID      AS ID
        , Hic.NAME     AS NAME
        , Hic.ParentID AS ParentEvent 
        , COUNT(*)-1   AS Depth    
FROM    ClassHierarchy_CTE CTE INNER JOIN hierarchy Hic
ON      CTE.CID = Hic.ID
WHERE   ClassID_Join IS NOT NULL
GROUP BY CTE.CID, Hic.NAME, Hic.ParentID

结果集

╔════╦══════════════════╦═════════════╦═══════╗
║ ID ║       NAME       ║ ParentEvent ║ Depth ║
╠════╬══════════════════╬═════════════╬═══════╣
║  0 ║ Happy Event      ║ NULL        ║     0 ║
║  1 ║ Sad Event        ║ NULL        ║     0 ║
║  2 ║ Very Happy Event ║ 0           ║     1 ║
║  3 ║ Very Sad Event   ║ 1           ║     1 ║
║  4 ║ Happiest Event   ║ 2           ║     2 ║
║  5 ║ Unpleasant Event ║ 1           ║     1 ║
╚════╩══════════════════╩═════════════╩═══════╝