【问题标题】:SQL Server 2008 CTE Calculate all ways between two pointsSQL Server 2008 CTE 计算两点之间的所有方式
【发布时间】:2015-03-06 20:02:13
【问题描述】:

我在 CTE 上尝试了很多东西,但仍然有问题 例如,我有一张这样的桌子:(在我的桌子上我有 6 873 368 行

+--------+----------+---------+
| SOURCE | DEST     | DISTANCE| 
+--------+----------+---------+
|  1     | 1        |  125    | 
|  1     | 2        |  100    | 
|  1     | 3        |  002    | 
|  1     | 4        |  058    | 
|  2     | 1        |  000    | 
|  2     | 2        |  050    | 
|  2     | 3        |  125    | 
|  2     | 4        |  785    | 
|  3     | 1        |  000    | 
|  3     | 2        |  050    | 
|  3     | 3        |  125    | 
|  3     | 4        |  785    | 
+--------+----------+---------+

我想一路走 grom 来源:例如 1 到目的地 4 对于某些线路,它与 CTE 完美配合,但对于数字线路,我花了很长时间(一些解决方案超过 29 分钟)。

我试试这个:

;WITH T_Route (CONNECTION_DEST, STEPS, WEIGTH, WAY, RESSOURCE_SRC,RESSOURCE_DEST,RESSOURCE_TYPE) 
AS
   (SELECT DISTINCT C.CONNECTION_SRC 
                   , 0
                   , 0
                   , @SRC
                   , @SRC
                   , @SRC
                   , 1
    FROM #CheminCircuit AS C
    WHERE C.RESSOURCE_SRC = @SRC

    UNION  ALL

    SELECT arrival.CONNECTION_DEST
            , departure.STEPS + 1
            , departure.WEIGTH + arrival.VOL
            , departure.WAY + ',' + arrival.RESSOURCE_DEST 
            , departure.RESSOURCE_DEST
            , arrival.RESSOURCE_DEST
            , arrival.RESSOURCE_TYPE
    FROM #CheminCircuit AS arrival
    INNER JOIN T_Route AS departure ON departure.CONNECTION_DEST = case when departure.STEPS < @STEPS then arrival.CONNECTION_SRC else 0 end -- AND arrival.RESSOURCE_SRC not like '%' + @DEST + '%' AND departure.STEPS < @STEPS
    WHERE departure.WAY NOT LIKE '%,' + arrival.RESSOURCE_DEST + '%' 
    AND (arrival.RESSOURCE_TYPE NOT IN (SELECT T.[Index] FROM Type_Ressource T WHERE T.[Index] IN (1)) OR arrival.RESSOURCE_DEST IN (@SRC,@DEST))
    )
,SHORT (WEIGTH)
AS
    (SELECT WEIGTH
     FROM T_Route
     WHERE  RESSOURCE_DEST  = @DEST)

SELECT *
FROM  T_Route AS T

我的输出是这样的:

+--------+----------+----------------+--------+--------+
| SOURCE | DEST     | DISTANCE       | TIME   |STEPS   |
+--------+----------+----------------+--------+--------+
|  1     | 4        |  1->2->3->4    |  285   | 2      |
|  1     | 4        |  1->4          |  183   | 0      |
|  1     | 4        |  1->3->4       |  185   | 1      |
|  1     | 4        |  1->2->4       |  283   | 1      |
+--------+----------+---------+------+--------+--------+

我只是想计算我不需要从所有点一路走来的路,例如从 A 到 B 的路:) 如果可能的话,你知道我怎么能在更短的时间内完成吗?

我尝试了很多东西,但我不知道如何在达到期望值时停止 CTE?

我在 select * from CTE 语句之前有这个结果:

    +--------+----------+----------------+--------+--------+
    | SOURCE | DEST     | DISTANCE       | TIME   |STEPS   |
    +--------+----------+----------------+--------+--------+
    |  1     | 4        |  1->2->4->3    |  285   | 2      |
    |  1     | 4        |  1->4->1       |  183   | 0      |
    |  1     | 4        |  1->3->4->2->1 |  185   | 1      |
    |  1     | 4        |  1->2->4       |  283   | 1      |
    +--------+----------+---------+------+--------+--------+

但我想在 CTE 期间停止结果到 dest : 4 谢谢:)

【问题讨论】:

  • 什么数据库系统,哪个版本? SQL 只是结构化查询语言 - 许多数据库系统使用的语言 - SQL NOT 数据库产品...这样的东西通常是特定于供应商的 - 所以我们真的需要知道您使用的是什么数据库系统......(请相应地更新标签)
  • 对不起,我不准确,我使用的是 sql server 2008
  • CTE一路计算,用这种方式耗时太长,我尝试修改cte中的内连接,但没有结果
  • 只看一下查询,最大的性能杀手很可能是您的 join 和 where 子句,尤其是 NOT LIKE 不可分割(以 % 开头),但您可能不会能够摆脱它。 CTE 在大型数据集中确实会受到影响,因此将查询更改为使用带有 while 循环的索引临时表来执行递归可能不那么漂亮,但可以加快速度。
  • 但我不确定我的内部连接语句是否正确或我的 where 子句

标签: sql sql-server-2008 common-table-expression recursive-query


【解决方案1】:

我喜欢您对 CTE 的看法,但使用 NOT LIKE '% + .. 效率非常低。

我已经尝试使用另一种方法进行这种比较,使用二进制数学而不是字符串比较!

基本上,我们将“方式”存储为 2^(Destination) 的总和。
因此,目的地 ID 为 1 和 3 的路线将是 2^1 + 2^3 = 2 + 8 = 10
希望您能看到这是一种有效的方式来存储所有访问过的目的地(但不是按顺序)。
然后查看过去是否访问过某个步骤,我们只比较有问题的位

您可以通过采用2 MOD (2^(current destinationID + 1)) 来做到这一点(基本上从存储方式二进制中删除所有较高的目的地 - 只留下 ID 小于或等于当前目的地的目的地),并检查此数字是否小于2 ^ (current destination ID).

注意 - 使用单个字段来存储二进制方式字段将允许 Ids 从 0 到 30 以整数作为数据类型(2^31 - 1 是可以存储的最大数量)
所以使用 INT 的最大 ID 为 30
如果我们使用 BIGINT,则最大 ID 为 62
如果我们使用 DECIMAL(38,0),则最大 ID 为 125(尽管它是 17 字节/136 位字段,但 maxID 为 10^38 -1)

不知道我解释得有多好,所以在实践中......

DECLARE @CheminCircuit TABLE([Source] INT, Dest INT, DISTANCE INT)
INSERT @CheminCircuit
        ( Source, Dest, DISTANCE )
VALUES  ( 1,1,125), (1,2,100),(1,3,2),(1,4,58),(2,1,0),
        (2,2,50),(2,3,125),(2,4,785),(3,1,0),(3,2,50),(3,3,125),(3,4,785)

DECLARE @maxSteps INT
SELECT @maxSteps = COUNT(DISTINCT Dest) 
       FROM @CheminCircuit AS cc WHERE Dest <> @src

; WITH T_Route ([Source], [Dest], Distance, Way, WayBin, STEPS)
AS(
    SELECT Source, 
       Dest, 
       DISTANCE, 
       CAST(CAST(@src AS NVARCHAR(255)) + '->' + CAST(Dest AS NVARCHAR(255)) AS NVARCHAR(255)), 
       POWER(2,Source) + POWER(2,Dest), 
       1
    FROM @CheminCircuit AS cc WHERE Source = @src AND cc.Dest <> cc.Source

    UNION ALL

    SELECT T_Route.Source, cc.Dest, 
           T_Route.Distance + cc.DISTANCE, 
           CAST( T_Route.Way + '->' + CAST(cc.Dest AS NVARCHAR(255)) AS NVARCHAR(255)), 
           T_Route.WayBin + POWER(2,cc.Dest), 
           T_Route.STEPS + 1
    FROM @CheminCircuit AS cc
        JOIN T_Route ON T_Route.Dest = cc.Source
    WHERE T_Route.STEPS < @maxSteps 
        AND T_Route.Dest <> @dst AND cc.Dest <> cc.Source 
        AND (T_Route.WayBin % POWER(2, cc.Dest+1) ) < POWER(2,cc.Dest)
)
SELECT * FROM T_Route WHERE Dest = @dst

这给出了期望的结果(或多或少)

Source  Dest    Distance    Way         WayBin  STEPS
------  ----    --------    --          ------  -----
1       4       58          1->4        18      1
1       4       787         1->3->4     26      2
1       4       837         1->3->2->4  30      3
1       4       885         1->2->4     22      2
1       4       1010        1->2->3->4  30      3

您还会注意到,我还在检查我们是否没有超出 CTE 自联接中的最大步骤数

【讨论】:

  • 嘿 :) 感谢这个答案,我在自己的桌子上试过,但它返回了一个错误,可能是由于目的地的力量,我有这个:Erreur de dépassement arithmétique pour le type int,价值 = 281474976710656.000000。在我的表中,我有 6034 个源和目标,也许是问题所在?
  • 嗨 - 是的,此方法仅适用于源和目标的较小 ID 值 - 根据上面的编辑 - INT 最多 30,DECIMAL 最多 125(38,0 ) .从技术上讲,您可以通过拥有多个字段并在它们之间拆分值来覆盖更多 ID——但这意味着您的数据需要 49 个字段!我怀疑它是否会在那些更高的 ID 上工作,因为精度会丢失。所以我认为可能会放弃 CTE 的想法,而是考虑做一个 while 循环,而不是在其中建立 2 个表(路由和 routeItem) - 每次迭代都从一个复制到下一个
  • NB - 如果您只想在到达目的地时停止 CTE,您只需将 AND departure.RESSOURCE_DEST &lt;&gt; @DEST 添加到 CTE 递归部分的 WHERE 子句中
  • 考虑一下,尽管使用 VARBINARY(8000) 作为存储列仍然可以使此方法工作,但这需要更多考虑。如果我能解决,我会更新答案!
猜你喜欢
  • 2013-11-13
  • 1970-01-01
  • 2020-06-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-17
  • 1970-01-01
  • 2011-04-11
相关资源
最近更新 更多