SQL select查询中的排序结果
假设我有一个名为Events的表,其数据如下所示:SQL select查询中的排序结果,sql,sql-server,tsql,select,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 | H
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
我如何查询此表以获得返回的结果
具有非null ParentEvent的事件直接出现在ID与ParentEvent匹配的事件之后
ParentEvent为null的事件的深度为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中执行此操作,请继续回答。测试数据
查询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
结果集
以下查询都返回您要求的确切结果集。所有这些工作都是通过计算到根节点的完整路径,并使用某种技术使该路径能够按顺序排列 SQLServer2008及以上版本。在这里,通过转换为hierarchyid数据类型,SQLServer可以正确地处理排序
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);
SQLServer2005及以上版本。我们可以将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层的深度。这只是补充了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
Oracle使用CONNECT BY子句哪个SQL Server/ASE版本?是-但OP也要求提供任何解决方案。。也许在调查中会有所帮助我没有搜索,但你可以在T中找到一些文章或参考资料,通过解决方案进行连接SQL@M.Ali:您似乎误解了问题-您需要阅读问题中所述的条件,以理解为什么结果按原来的顺序排列。在任何情况下,请不要在其他人的问题中编辑代码和输出,因为这样做会完全改变问题的含义。这如何回答要求事件,即具有非空ParentEvent的事件直接出现在ID与ParentEvent匹配的事件之后?在父事件1下面有一个ParentID为0的非常高兴的事件。@ErikE CID、NAME和ParentID是从表本身提交的,我刚刚更改了列的名称。只有深度列来自cte,在cte中,我刚刚计算了上面所有子记录的所有父记录-1,这是它在所需结果集中的显示方式。所需结果集显示ID顺序为0、2、4、1、3、5。您的查询显示ID顺序为0、1、2、3、4、5。这是不正确的。@ErikE问题文本:只要结果满足前两个条件,结果出现的顺序就不符合matterI的要求!请注意其中一个条件:具有非null ParentEvent的事件将直接出现在ID与ParentEvent匹配的事件之后。结果集中的行3“非常悲伤的事件”,1,1不是直接在其父事件1之后!这是如何满足条件的?问题文本:只要结果满足前两个条件,结果出现的顺序就不会改变matter@ErikE直到我发布了我的答案后,我才看到你的答案。你也有同样的想法,但在我之前就发布了。同样的varbinary转换可以完成,允许高达2000级的深度?8000/码发现了,但也许我弄错了,安德烈,这不是第一次,谢谢你让我保持警惕!请注意,您不需要先转换为int,而且varbinary2只允许ID值高达65535。@ErickE谢谢。你是对的。我删除了转换为int的命令。我意识到varbinary2是有限的,但我认为这对于这个例子来说是可行的。我最初有varbinary3,但它似乎有点过分了。
;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