【问题标题】:Find all distinct paths in a directed graph and compute minimal cost在有向图中查找所有不同的路径并计算最小成本
【发布时间】:2013-12-02 14:34:41
【问题描述】:

我们有以下 Oracle 表:

CREATE TABLE graph
( company VARCHAR2(10)
, from VARCHAR2(15)
, to VARCHAR2(15)
, cost NUMBER(18,2));

有以下数据:

INSERT INTO graph VALUES('Lufthansa', 'San Francisco', 'Denver', 1000);
INSERT INTO graph VALUES('Lufthansa', 'San Francisco', 'Dallas', 10000);
INSERT INTO graph VALUES('Lufthansa', 'Denver', 'Dallas', 500);
INSERT INTO graph VALUES('Lufthansa', 'Denver', 'Chicago', 2000);
INSERT INTO graph VALUES('Lufthansa', 'Dallas', 'Chicago', 600);
INSERT INTO graph VALUES('Lufthansa', 'Dallas', 'New York', 2000);
INSERT INTO graph VALUES('Lufthansa', 'Chicago', 'New York', 3000);
INSERT INTO graph VALUES('Lufthansa', 'Chicago', 'Denver', 2000);

我们被要求创建一个表:

CREATE TABLE paths
( from VARCHAR2(15)
, to VARCHAR2(15)
, minimal_cost NUMBER);`

paths 中的一行应表明“我可以从Y 以最低成本C 到达X

到目前为止我已经尝试过:

INSERT INTO paths ("from", "to")
SELECT     DISTINCT CONNECT_BY_ROOT "from" AS "From", "to" AS "To"
FROM       graph
START WITH "from" in (SELECT "from" FROM graph) 
CONNECT BY NOCYCLE PRIOR "to" = "from";

(我已经用我的语言重命名了表名和属性,因此如果您尝试直接运行这些语句,可能会出现语法错误。)

上述语句几乎在所有方面都创建了重复项。另外,我不知道应该如何计算cost。我需要创建一个PL/SQL 程序吗? Oracle 是否支持这类查询?

【问题讨论】:

  • 你的预期输出是什么?

标签: sql oracle graph path


【解决方案1】:

通过稍微滥用SYS_CONNECT_BY_PATH 和一个使用正则表达式从列表中提取数字的函数,您可以做到:

SQL Fiddle

Oracle 11g R2 架构设置

CREATE TABLE graph
( company VARCHAR2(10)
, "from" VARCHAR2(15)
, "to" VARCHAR2(15)
, cost NUMBER(18,2))
/

BEGIN
  INSERT INTO graph VALUES('Lufthansa', 'San Francisco', 'Denver', 1000);
  INSERT INTO graph VALUES('Lufthansa', 'San Francisco', 'Dallas', 10000);
  INSERT INTO graph VALUES('Lufthansa', 'Denver', 'Dallas', 500);
  INSERT INTO graph VALUES('Lufthansa', 'Denver', 'Chicago', 2000);
  INSERT INTO graph VALUES('Lufthansa', 'Dallas', 'Chicago', 600);
  INSERT INTO graph VALUES('Lufthansa', 'Dallas', 'New York', 2000);
  INSERT INTO graph VALUES('Lufthansa', 'Chicago', 'New York', 3000);
  INSERT INTO graph VALUES('Lufthansa', 'Chicago', 'Denver', 2000);
END;
/

CREATE TABLE paths
( "from" VARCHAR2(15)
, "to" VARCHAR2(15)
, minimal_cost NUMBER)
/

CREATE OR REPLACE FUNCTION sum_costs (
  vals VARCHAR2
) RETURN NUMBER
AS
  num_vals SIMPLE_INTEGER := REGEXP_COUNT( vals, '\d+' );
  total graph.cost%TYPE := 0;
BEGIN
  FOR i IN 1 .. num_vals LOOP
    total := total + TO_NUMBER( REGEXP_SUBSTR( vals, '\d+', 1, i ) );
  END LOOP;
  RETURN total;
END;
/

查询 1

WITH costs AS (
  SELECT     CONNECT_BY_ROOT "from" AS "from",
             "to",
             sum_costs( SYS_CONNECT_BY_PATH ( cost, ',' ) ) AS total_cost
  FROM       graph
  WHERE      CONNECT_BY_ROOT "from" <> "to"
  CONNECT BY NOCYCLE PRIOR "to" = "from"
)
SELECT "from",
       "to",
       MIN( total_cost )
FROM   costs
GROUP BY "from", "to"

Results

|          FROM |       TO | MIN(TOTAL_COST) |
|---------------|----------|-----------------|
|        Dallas |  Chicago |             600 |
|       Chicago | New York |            3000 |
|       Chicago |   Denver |            2000 |
| San Francisco |   Denver |            1000 |
|        Dallas | New York |            2000 |
|        Denver |  Chicago |            1100 |
| San Francisco | New York |            3500 |
|        Denver |   Dallas |             500 |
|        Dallas |   Denver |            2600 |
|        Denver | New York |            2500 |
| San Francisco |   Dallas |            1500 |
| San Francisco |  Chicago |            2100 |
|       Chicago |   Dallas |            2500 |

这也为每对目的地获得了一条最佳路线:

查询 2

WITH costs AS (
  SELECT     CONNECT_BY_ROOT "from" AS "from",
             "to",
             SUBSTR( SYS_CONNECT_BY_PATH ( "from", ',' ), 2 ) || ',' || "to" AS route,
             sum_costs( SYS_CONNECT_BY_PATH ( cost, ',' ) ) AS total_cost
  FROM       graph
  WHERE      CONNECT_BY_ROOT "from" <> "to"
  CONNECT BY NOCYCLE PRIOR "to" = "from"
)
SELECT
       "from",
       "to",
       MIN( route ) KEEP ( DENSE_RANK FIRST ORDER BY total_cost ) AS optimal_route,
       MIN( total_cost ) AS minimum_cost
FROM   costs
GROUP BY "from", "to"

Results

|          FROM |       TO |                        OPTIMAL_ROUTE | MINIMUM_COST |
|---------------|----------|--------------------------------------|--------------|
|        Dallas |   Denver |                Dallas,Chicago,Denver |         2600 |
|        Dallas |  Chicago |                       Dallas,Chicago |          600 |
|        Dallas | New York |                      Dallas,New York |         2000 |
|        Denver |   Dallas |                        Denver,Dallas |          500 |
|        Denver |  Chicago |                Denver,Dallas,Chicago |         1100 |
|        Denver | New York |               Denver,Dallas,New York |         2500 |
|       Chicago |   Dallas |                Chicago,Denver,Dallas |         2500 |
|       Chicago |   Denver |                       Chicago,Denver |         2000 |
|       Chicago | New York |                     Chicago,New York |         3000 |
| San Francisco |   Dallas |          San Francisco,Denver,Dallas |         1500 |
| San Francisco |   Denver |                 San Francisco,Denver |         1000 |
| San Francisco |  Chicago |  San Francisco,Denver,Dallas,Chicago |         2100 |
| San Francisco | New York | San Francisco,Denver,Dallas,New York |         3500 |

还有一个纯 SQL 解决方案:

查询 3

WITH Routes AS (
  SELECT     CONNECT_BY_ROOT "from" AS "from",
             "to",
             SUBSTR( SYS_CONNECT_BY_PATH ( "from", ',' ), 2 ) || ',' || "to" AS route,
             cost
  FROM       graph
  WHERE      CONNECT_BY_ROOT "from" <> "to"
  CONNECT BY NOCYCLE PRIOR "to" = "from"
),
costs AS (
  SELECT r."from",
         r."to",
         r.route,
         SUM( s.cost ) AS total_cost
  FROM   Routes r
         INNER JOIN
         Routes s
         ON (    r."from" = s."from"
             AND LENGTH( r.route ) >= LENGTH( s.route )
             AND SUBSTR( r.route, 1, LENGTH( s.route ) ) = s.route )
  GROUP BY r."from", r."to", r.route
)
SELECT "from",
       "to",
       MIN( route ) KEEP ( DENSE_RANK FIRST ORDER BY total_cost ) AS optimal_route,
       MIN( total_cost )
FROM   costs
GROUP BY "from", "to"

Results

|          FROM |       TO |                        OPTIMAL_ROUTE | MIN(TOTAL_COST) |
|---------------|----------|--------------------------------------|-----------------|
|        Dallas |   Denver |                Dallas,Chicago,Denver |            2600 |
|        Dallas |  Chicago |                       Dallas,Chicago |             600 |
|        Dallas | New York |                      Dallas,New York |            2000 |
|        Denver |   Dallas |                        Denver,Dallas |             500 |
|        Denver |  Chicago |                Denver,Dallas,Chicago |            1100 |
|        Denver | New York |               Denver,Dallas,New York |            2500 |
|       Chicago |   Dallas |                Chicago,Denver,Dallas |            2500 |
|       Chicago |   Denver |                       Chicago,Denver |            2000 |
|       Chicago | New York |                     Chicago,New York |            3000 |
| San Francisco |   Dallas |          San Francisco,Denver,Dallas |            1500 |
| San Francisco |   Denver |                 San Francisco,Denver |            1000 |
| San Francisco |  Chicago |  San Francisco,Denver,Dallas,Chicago |            2100 |
| San Francisco | New York | San Francisco,Denver,Dallas,New York |            3500 |

【讨论】:

  • 第三个查询简直太棒了!你真是个天才!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-07-21
  • 1970-01-01
  • 1970-01-01
  • 2018-10-16
  • 1970-01-01
  • 2013-10-25
  • 1970-01-01
相关资源
最近更新 更多