原始数据表是
+--------+--------+--------+ | 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)。
如何解决这个问题?
答案 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)