使用SQL将有向边转换为无向边

时间:2013-11-15 07:01:18

标签: sql postgresql graph

原始数据表是

+--------+--------+--------+
| node_1 | node_2 | weight |
+--------+--------+--------+
|      1 |      2 |      5 |
|      1 |      3 |     10 |
|      2 |      1 |     21 |
|      1 |      4 |     15 |
+--------+--------+--------+

这是一个有向加权图。我想要的是将有向图转换为无向图,如

+--------+--------+--------+
| node_1 | node_2 | weight |
+--------+--------+--------+
|      1 |      2 |     26 |
|      1 |      3 |     10 |
|      1 |      4 |     15 |
+--------+--------+--------+

有边(1,3)并不意味着存在边(3,1)。

如何解决这个问题?

3 个答案:

答案 0 :(得分:2)

看起来您想要对无向节点的权重求和。 你的例子不清楚相同节点之间是否有多个有向边(即1-> 2,1-> 2,1-> 2),但在这里我假设它们也应该合并

我没有一个PSQL数据库可以方便地测试它,但这是非常普通的SQL,所以你应该没问题。

免责声明:请先在测试台上使用ROLLBACK进行尝试。不要相信你的真实数据是陌生人在互联网上的建议,特别是如果他们有1个代表: - )

这里的诀窍是很难对两个可互换的列(node_1,node_2)进行分组,这就是为什么我首先将它们重新排列为不可互换/有序的

BEGIN TRANSACTION

  -- 1a) Make a temporary place to hold our values
  CREATE TEMP TABLE tempnodes (int low_node, int high_node, int weight)
    ON COMMIT DROP;

  -- 1b) consistently write the nodes to temp
  -- making sure node_1 always <= node_2
  -- this ordering helps our grouping in the next step
  INSERT INTO temptable (low_node, high_node, weight)
    SELECT least(node_1, node_2), greatest(node_1, node_2), weight
    FROM oldtable;

  -- 2a) Create a place for our new data
  -- Alternatively, you could truncate the old table
  -- and write the new values back there
  -- however, this way we can go back if/when we make a mistake
  CREATE TABLE newtable(int low_node, int high_node, int summed_weight);

  -- 2b) merge the edges 
  INSERT INTO newtable( low_node, high_node, summed_weight)
    SELECT low_node, high_node, sum(weight)
    FROM temptable
    GROUP BY low_node, high_node;

ROLLBACK TRANSACTION;
--COMMIT TRANSACTION;

答案 1 :(得分:2)

如果您不想(或者不需要)实际更改数据,可以通过一次选择来完成此操作:

select least(node_1, node_2) as node_1, 
       greatest(node_2, node_1) as node_2, 
       sum(weight) as weight
from graph
group by least(node_1, node_2), greatest(node_2, node_1)
order by 1,2;

如果您同时需要定向和无向图,则可以将上述内容转换为视图。

与pratik的答案类似,这可以合并为一个更改基础表的语句:

with directed_graph as (
  select least(node_1, node_2) as node_1, 
         greatest(node_2, node_1) as node_2, 
         sum(weight) as weight
  from graph
  group by least(node_1, node_2), greatest(node_2, node_1)
), 
new_graph as (
  update graph 
    set weight = dg.weight
  from directed_graph dg
  where (graph.node_1, graph.node_2) = (dg.node_1, dg.node_2)
  returning graph.*
)
delete from graph 
where not exists (select 1 
                  from new_graph ng
                  where ng.node_1 = graph.node_1 
                    and ng.node_2 = graph.node_2);

这是一个SQLFiddle演示:http://sqlfiddle.com/#!15/ad5b7/1

答案 2 :(得分:1)

你需要使用update并删除这两个语句来解决这个问题。

UPDATE test_prit_1 t_1
   SET weight = weight + coalesce((SELECT t_2.weight
                               FROM test_prit_1 t_2
                              WHERE t_2.node_1 = t_1.node_2
                                AND t_2.node_2 = t_1.node_1
                                AND t_2.node_1 > t_2.node_2),
                             0)

然后删除语句以删除额外的行(在给定的示例中node_1 = 2和node_2 = 1)

DELETE FROM test_prit_1 t_1
 WHERE EXISTS (SELECT 1
          FROM test_prit_1 t_2
         WHERE t_2.node_1 = t_1.node_2
           AND t_2.node_2 = t_1.node_1
           AND t_2.node_2 > t_2.node_1)