在SQL中向后走树

时间:2010-10-07 23:10:26

标签: sql loops

我有一个非常简单的模式表:

CREATE TABLE q(
 orig INTEGER NOT NULL,
 dest INTEGER NOT NULL,
 cost  FLOAT,
 PRIMARY KEY (orig, dest)
);

我需要以最小化成本的方式向后走这张桌子。让我解释一下。

我需要15点的巡视,点数可以是origdest。我的算法是从最后dest到最初orig的回溯。所以这就是我拼写出来的方式:

  

鉴于最终dest,请找到orig,该dest将链接到所述cost,且orig最少。相应的dest成为新的dest;循环这15次。

我们假设我知道最后10是数字orig。允许我以dest最小化方式找到导致cost-的{​​{1}}的SQL语句是:

SELECT orig FROM q WHERE cost = (SELECT MIN(cost) FROM q WHERE dest = 10);

现在我将使用上面函数返回的orig来查找前一个点(假设它已返回,比如指向5):

SELECT orig FROM q WHERE cost = (SELECT MIN(cost) FROM q WHERE dest = 5);

我可以这样继续,直到得到15分。

如何在SQL中进行有效的查询?我的桌子有5000万行。

2 个答案:

答案 0 :(得分:0)

这是一个假设您正在使用SQL Server 2005+的查询。它使用公用表表达式(CTE)。此示例实际上返回具有所选orig和dest的具有累积成本的所有路径。它还显示路径中的点数。它可以被改变以返回最佳选择(例如,最低成本,最少步骤等)。

我不知道表现如何。但是,索引会有所帮助。

WITH Paths AS  -- Get list of all paths
    ( SELECT ROW_NUMBER() OVER (ORDER BY orig) AS PathNumber, orig,
        dest, cost, 1 AS points
    FROM q

    UNION ALL

    SELECT Paths.PathNumber, Paths.orig,
        q.dest, q.cost, paths.points + 1 AS points
    FROM Paths
    JOIN q ON Paths.dest = q.orig
    WHERE Paths.points < 15
    )
    , PathsRows AS  -- Get total points in each path
    ( SELECT COUNT(*) OVER (PARTITION BY PathNumber) AS TotalPoints, PathNumber
        , orig, dest, cost, points
    FROM Paths
    )
    , PathsSum AS  -- summarize for each path
    ( SELECT PathNumber,
        MIN(CASE WHEN points = TotalPoints THEN orig END) AS orig,
        MAX(CASE WHEN points = TotalPoints THEN dest END) AS dest,
        SUM(cost) AS cost, MAX(points) AS points
    FROM PathsRows
    GROUP BY PathNumber
    )

SELECT PathNumber, orig, dest, cost, points
FROM PathsSum
WHERE dest = 4
    and orig = 1
ORDER BY PathNumber, points

答案 1 :(得分:0)

简短回答:你不能。

更长的答案:假设我已经正确理解了你的问题,你可以编写一个尽可能高效的SQL语句,但是在一个合理的时间范围内返回结果是非常不可能的 - 比如,宇宙的年龄如此远。

假设您的5000万行表没有重复,并将直接路径从所有位置映射到所有其他位置,那么所包含的位置数大约是5000万的平方根 - 即。有点超过7070个地点。

因此可以采用的路径数量为7070 x 7069 x 7068 ... x 7056,或换句话说,大于7000 ^ 15(约4.75 x 10 ^ 57)。

最终,这是Travelling Salesman Problem的变体 - 基于SQL的暴力方法完全不适合解析它,数据集很大。