图形问题:在 SQL 服务器中通过 NOCYCLE 先前替换连接?

Posted

技术标签:

【中文标题】图形问题:在 SQL 服务器中通过 NOCYCLE 先前替换连接?【英文标题】:Graph problems: connect by NOCYCLE prior replacement in SQL server? 【发布时间】:2011-10-29 16:06:31 【问题描述】:

问题:

我有以下(有向)图:

还有这张桌子:

CREATE TABLE [dbo].[T_Hops](
    [UID] [uniqueidentifier] NULL,
    [From] [nvarchar](1000) NULL,
    [To] [nvarchar](1000) NULL,
    [Distance] [decimal](18, 5) NULL
) ON [PRIMARY]

GO

还有这个内容:

      INSERT INTO [dbo].[T_Hops]             ([UID]             ,[From]             ,[To]             ,[Distance])       VALUES             (newid()              ,'A'              ,'E'              ,10.00000              );   
      INSERT INTO [dbo].[T_Hops]             ([UID]             ,[From]             ,[To]             ,[Distance])       VALUES             (newid()              ,'E'              ,'D'              ,20.00000              );   
      INSERT INTO [dbo].[T_Hops]             ([UID]             ,[From]             ,[To]             ,[Distance])       VALUES             (newid()              ,'A'              ,'B'              ,5.00000              );   
      INSERT INTO [dbo].[T_Hops]             ([UID]             ,[From]             ,[To]             ,[Distance])       VALUES             (newid()              ,'B'              ,'C'              ,10.00000              );   
      INSERT INTO [dbo].[T_Hops]             ([UID]             ,[From]             ,[To]             ,[Distance])       VALUES             (newid()              ,'C'              ,'D'              ,5.00000              );   
      INSERT INTO [dbo].[T_Hops]             ([UID]             ,[From]             ,[To]             ,[Distance])       VALUES             (newid()              ,'A'              ,'F'              ,2.00000              );   
      INSERT INTO [dbo].[T_Hops]             ([UID]             ,[From]             ,[To]             ,[Distance])       VALUES             (newid()              ,'F'              ,'G'              ,6.00000              );   
      INSERT INTO [dbo].[T_Hops]             ([UID]             ,[From]             ,[To]             ,[Distance])       VALUES             (newid()              ,'G'              ,'H'              ,3.00000              );   
      INSERT INTO [dbo].[T_Hops]             ([UID]             ,[From]             ,[To]             ,[Distance])       VALUES             (newid()              ,'H'              ,'D'              ,1.00000              );   

现在我可以像这样查询从点 x 到点 y 的最佳连接:

WITH AllRoutes 
(
     [UID]
    ,[FROM]
    ,[To]
    ,[Distance]
    ,[Path]
    ,[Hops]
)
AS
(
    SELECT 
         [UID]
        ,[FROM]
        ,[To]
        ,[Distance]
        ,CAST(([dbo].[T_Hops].[FROM] + [dbo].[T_Hops].[To]) AS varchar(MAX)) AS [Path]
        ,1 AS [Hops]
      FROM [dbo].[T_Hops]
      WHERE [FROM] = 'A'

    UNION ALL


    SELECT 
         [dbo].[T_Hops].[UID]
        --,[dbo].[T_Hops].[FROM]
        ,Parent.[FROM]
        ,[dbo].[T_Hops].[To]
        ,CAST((Parent.[Distance] + [dbo].[T_Hops].[Distance]) AS [decimal](18, 5)) AS distance
        ,CAST((Parent.[Path] + '/' + [dbo].[T_Hops].[FROM] + [dbo].[T_Hops].[To]) AS varchar(MAX)) AS [Path]
        ,(Parent.[Hops] + 1) AS [Hops]
     FROM [dbo].[T_Hops]

    INNER JOIN AllRoutes AS Parent 
            ON Parent.[To] = [dbo].[T_Hops].[FROM] 

)

SELECT TOP 100 PERCENT * FROM AllRoutes


/*
WHERE [FROM] = 'A' 
AND [To] = 'D'
AND CHARINDEX('F', [Path]) != 0 -- via F
ORDER BY Hops, Distance ASC
*/

GO

现在我想创建一个无向图,例如,我还可以得到 从D到A的路径

我从一个最简单的改变开始,然后只为高清广告反方向。

INSERT INTO [dbo].[T_Hops]
           ([UID]
           ,[From]
           ,[To]
           ,[Distance])
     VALUES
           (newid() --<UID, uniqueidentifier,>
           ,'D' --<From, nvarchar(1000),>
           ,'H' --<To, nvarchar(1000),>
           ,1 --<Distance, decimal(18,5),>
           )
GO

现在,正如预期的那样,我的查询引发了异常:

无限递归/超出最大递归级别 (100)

因为现在可能的连接数是无限的。

现在在 Oracle 中,您可以使用“先验连接”而不是树来执行相同的操作。 如果可能出现循环问题(无限递归),您只需添加 NOCYCLE 到 CONNECT BY PRIOR,使其成为“CONNECT BY NOCYCLE PRIOR”

现在在 MS-SQL 中,我通过添加以下内容来修复该行为:

AND Parent.[Path] NOT LIKE '%' + [dbo].[T_Hops].[FROM] + '/%'

到内部连接子句,本质上是模拟 NOCYCLE。

但是,由于 LIKE 基本上是 strstr(或更糟糕的 strcasestr), 因此比检查父元素数组要慢得多, 我非常担心性能。

毕竟这只是一个例子,我打算基本添加数据 为整个国家。 所以最终结果可能会非常缓慢。

其他人有更好(=更快)的方法来替换 MS SQL 中的 NOCYCLE 吗?

或者这是我没有其他选择,只能切换到 Oracle(以可接受的速度执行此操作)的点?

注意: 任何临时表(大量数据)解决方案都会变慢, 因为临时表将被交换到硬盘 当内存不足时(绝对确定)。

任何使用函数和表值函数的解决方案也是如此。

【问题讨论】:

自我注意:在这里也问过:social.msdn.microsoft.com/Forums/en-US/transactsql/thread/… 自我注意:PostGre 的解决方案:***.com/questions/25058906/nocycle-in-postgres 你似乎精通这门语言。为什么不使用一个过程来实现许多有据可查的全对最短路径算法之一的 SQL 等效项,而不是使用有点花哨的递归呢?我的猜测是,这将比尝试优化性能较差的算法的查询更好地提高性能。 【参考方案1】:

为了提高选择性能,在永久表中存储节点之间的可能路径

TABLE T_Hops_Path
(
  FromNode,
  ToNode,
  HopCount,
  TotalDistance
)

如果您的树结构不经常更改,您可以编写一个存储过程,每 N 小时生成一次该表。

【讨论】:

并创建一个存储过程,该存储过程使用日期时间后缀创建该表,将最新的 n 后缀写入您提到的存储过程可用于创建动态 SQL 字符串的表中。嗯,大拼贴,但它可以工作。

以上是关于图形问题:在 SQL 服务器中通过 NOCYCLE 先前替换连接?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 SQL 中通过多列连接两个表?

如何在调用脚本中通过正则表达式执行 sql 脚本?

在 PySpark 中通过 JDBC 实现 SQL Server

在 Spark SQL Query 中通过 Repartition 重用 Exchange

在 Spark SQL 中通过 COALESCE 减少分区

在 Oracle 12c 的 IDENTITY 列中通过 SQL * Loader 加载数据