[英]SQL Server CTE to find path from one ID to another ID
我在SQL Server 2008 R2数据库中有一个表,该表定义了不同状态之间的转换。
相关表格列示例:
TransitionID | SourceStateID | DestinationStateID | WorkflowID
--------------------------------------------------------------
1 | 17 | 18 | 3
2 | 17 | 21 | 3
3 | 17 | 24 | 3
4 | 17 | 172 | 3
5 | 18 | 17 | 3
6 | 18 | 24 | 3
7 | 18 | 31 | 3
8 | 21 | 17 | 3
9 | 21 | 20 | 3
10 | 21 | 141 | 3
11 | 21 | 184 | 3
...等
我的目标是能够定义起始 StateID, 终止 StateID和WorkflowID,并希望在该工作流中找到从起始StateID到终止StateID的逻辑路径。
例如,对于上表,如果我提供的起始StateID为17,结束的StateID为184,而WorkflowID为3,则可能以“路径”为17> 21> 184(或更理想的是,TransitionID 2> TransitionID 11)
好:
坏处:
实际上,几乎每个源/目标StateID都有循环引用(即,可能从SourceStateID 1到DestinationStateID 2,也可能是从SourceStateID 2到DestinationStateID 1的过渡)
从几乎任何已定义的开始StateID和结束StateID肯定都有多种可能的路径
我意识到这是我所追求的CTE方式,但是诚然,我发现CTE总体上很难掌握,而且在循环引用是一个有保证的问题时更是如此。
理想的解决方案是选择从起始StateID到结束StateID的最短路径,但是老实说,如果我能得到可以可靠地给我两个状态之间任何有效路径的东西,那么我将是这样。快乐。
那里的任何SQL专家都有您可以指出的方向吗? 老实说,我什至不知道从哪里开始预防循环问题,例如沿着17> 18> 17> 18> 17> 18的路线前进……
令人不快的更新我提出了这个查询,它在情感上给我带来了伤害(在这篇文章中提供了一些帮助: https : //stackoverflow.com/a/11042012/3253311 ),但似乎可以正常工作。
DECLARE @sourceStatusId INT = 17
DECLARE @destinationStatusId INT = 24
DECLARE @workflowId INT = 3
DECLARE @sourceStatusIdString NVARCHAR(MAX) = CAST(@sourceStatusId AS NVARCHAR(MAX))
DECLARE @destinationStatusIdString NVARCHAR(MAX) = CAST(@destinationStatusId AS NVARCHAR(MAX))
DECLARE @workflowIdString NVARCHAR(MAX) = CAST(@workflowId AS NVARCHAR(MAX))
;WITH CTE ([Source], [Destination], [Sentinel]) AS
(
SELECT
CAST(t.[Source] AS NVARCHAR(MAX)) AS [Source],
CAST(t.[Destination] AS NVARCHAR(MAX)) AS [Destination],
[Sentinel] = CAST([Source] AS NVARCHAR(MAX))
FROM
Transitions t
WHERE
CAST([Source] AS NVARCHAR(MAX)) = @sourceStatusIdString AND
CAST([WorkflowID] AS NVARCHAR(MAX)) = @workflowIdString
UNION ALL
SELECT
CAST(CTE.[Destination] AS NVARCHAR(MAX)),
CAST(t.[Destination] AS NVARCHAR(MAX)),
CAST([Sentinel] AS NVARCHAR(MAX)) + CAST('|' AS NVARCHAR(MAX)) + CAST(CTE.[Destination] AS NVARCHAR(MAX))
FROM
CTE
JOIN Transitions t
ON CAST(t.[Source] AS NVARCHAR(MAX)) = CAST(CTE.[Destination] AS NVARCHAR(MAX))
WHERE
CHARINDEX(CTE.[Destination], Sentinel)=0
)
SELECT TOP 1
[Sentinel]
FROM
CTE
WHERE
LEFT([Sentinel], LEN(@sourceStatusIdString)) = @sourceStatusIdString AND
RIGHT([Sentinel], LEN(@destinationStatusIdString)) = @destinationStatusIdString
ORDER BY
LEN([Sentinel])
与Quassnoi的答案类似,但可防止在第一个元素之后开始的循环引用:
DECLARE @src int = 18, @dst int = 184;
WITH cte AS
(Select TransitionId, SourceStateId, DestinationStateID, SourceStateID as StartingState, 0 as Depth
, cast(TransitionId as varchar(max)) + ',' as IDPath
, cast(SourceStateID as varchar(max)) + ',' as States
FROM Transitions
WHERE SourceStateId = @src
UNION ALL
Select t.TransitionId, t.SourceStateId, t.DestinationStateID, StartingState, cte.Depth + 1
, cte.IDPath + cast(t.TransitionId as varchar(max)) + ','
, cte.States + cast(t.SourceStateID as varchar(max)) + ','
FROM cte JOIN Transitions t on cte.DestinationStateID = t.SourceStateId
and CHARINDEX(',' + cast(t.SourceStateID as varchar(max)) + ',', States) = 0 --prevent loop starting after first element
and t.DestinationStateID <> StartingState --prevent loop starting with first element
where cte.Depth < 10 -- prevent going too deep
)
select TransitionId, SourceStateId, DestinationStateID, Depth, left(IDPath, len(IDPath) - 1) IDPath, left(States, len(States) - 1) States
from cte
where DestinationStateID = @dst
order by Depth
WITH q (id, src, dst, sid, cnt, path) AS
(
SELECT transitionId, sourceStateId, destinationStateId, sourceStateId, 1,
CAST(transitionId AS VARCHAR(MAX)) path
FROM mytable
WHERE sourceStateId = 18
UNION ALL
SELECT m.transitionId, m.sourceStateId, m.destinationStateId,
CASE WHEN sid < sourceStateId THEN sid ELSE sourceStateId END, cnt + 1,
path + ', ' + CAST(transitionId AS VARCHAR(MAX))
FROM q
JOIN mytable m
ON m.sourceStateId = q.dst
AND m.sourceStateId <> q.sid
)
SELECT TOP 1
*
FROM q
WHERE dst = 184
ORDER BY
cnt DESC
这是我的看法...在Quassnoi的基础上,但比Kateract短;-)
http://www.sqlfiddle.com/#!6/322d3/4
WITH data AS (
SELECT TransitionId, SourceStateId, DestinationStateId,
'|' + CAST(transitionId AS VARCHAR(MAX)) as Path
FROM States
WHERE sourceStateId = 17
AND WorkflowID = 3
UNION ALL
SELECT m.TransitionId, m.sourceStateId, m.DestinationStateId,
Path + '|' + CAST(m.TransitionId AS VARCHAR(MAX))
FROM data d
JOIN States m ON
m.sourceStateId = d.DestinationStateId
AND CHARINDEX( '|' + convert(varchar(10),m.TransitionID) + '|', path) = 0
AND WorkflowID = 3
)
SELECT TOP 1 *
FROM data
WHERE DestinationStateId = 184
ORDER BY LEN(Path)
-- If you want the longest path... ORDER BY LEN(Path) DESC
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.