查找有向图中的所有不同路径并计算最低成本

时间:2013-12-02 14:34:41

标签: sql oracle graph path

我们有以下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中的一行应指示“我可以X Y以及C中的最低费用。

到目前为止我尝试过:

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是否支持这种类型的队列?

1 个答案:

答案 0 :(得分:4)

通过对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 |