【问题标题】:SQL Server CTE to find path from one ID to another IDSQL Server CTE 查找从一个 ID 到另一个 ID 的路径
【发布时间】:2016-08-07 22:43:14
【问题描述】:

我在 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

...等等

我的目标是能够定义 starting StateID、ending StateID 和 WorkflowID,并希望找到从起始 StateID 到结束的逻辑路径该工作流程中的 StateID。

例如对于上表,如果我提供的起始 StateID 为 17,结束 StateID 为 184,WorkflowID 为 3,我可以得到一个 17>21>184 的“路径”(或者更理想的是,TransitionID 2 > TransitionID 11)

善:

  • 定义的开始和结束 StateID总是在定义的 WorkflowID 中有一个可能路径

坏人:

  • 在几乎每个源/目标 StateID 上肯定都有循环引用(即,可能存在从 SourceStateID 1 到 DestinationStateID 2 以及从 SourceStateID 2 到 DestinationStateID 1 的转换

  • 从几乎任何定义的起始 StateID 和结束 StateID 肯定有多个可能的路径

我意识到这是我所追求的某种 CTE,但我承认我发现 CTE 通常很难掌握,而且当循环引用是一个有保证的问题时更是如此。

完美的解决方案是选择从起始 StateID 到结束 StateID 的最短路径,但老实说,在这一点上,如果我能得到可靠地给我 any 有效的东西两种状态之间的路径我会很高兴。

那里的任何 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])

【问题讨论】:

  • 没有多少 sql 甚至逻辑可以解决这个问题。您有一行具有 parentID 17 和一个 18 的孩子,在同一个组中,您有一个 18 的 parentID 和一个 17 的孩子。您甚至没有任何数据表明基点或起点是什么。你这里有个无解之谜。像这样想。鸡 17 岁,鸡蛋 18 岁。哪个先来?
  • @SeanLange 虽然我同意这是一个泡菜,但我尊重地不同意它“无法解决”。我刚刚提出了一个可行的解决方案,但是它的实施在情感层面上伤害了我。我现在要查看 Quassnoi 的答案,希望我不需要使用我想出的答案:)

标签: sql sql-server common-table-expression hierarchical hierarchical-query


【解决方案1】:
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

见小提琴:http://www.sqlfiddle.com/#!6/9342e/17

【讨论】:

  • 您的查询失败,开始值为 18,结束值为 184
  • @Quassnoi 我在尝试运行查询时遇到无限递归
【解决方案2】:

类似于 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

【讨论】:

  • 您的答案似乎运行得最快。我最终选择了一些稍微不同的东西,但这符合要求..谢谢!
【解决方案3】:

这是我的看法... 建立在 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-05-16
    • 2019-08-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-17
    • 1970-01-01
    相关资源
    最近更新 更多