在SQL中以最小的权重收集不同的邻居

时间:2018-11-07 14:33:02

标签: sql oracle

我有my_table t

id in_node out_node weight   

  1  'A'      'B'    17
  2  'B'      'A'     4  
  3  'C'      'A'    35   
  4  'A'      'D'    26
  ... 
  5  'C'      'G'    33     
  6  'X'      'Z'    12  
  7  'Z'      'Y'    15    
  8  'X'      'Y'    42   
  9  'K'      'M'    66   
  ...
 10  'A'      'Z'    20  

期望的行为

select id, in_count, in_weight, out_count,out_weight from t where  id = 10
      10      3          65          2        27      
             2,3,4      4+35+26     6,7      12+15      
       (1 is duplicate)                                              

计算不同in_neighbors数量的步骤:

  • $A: select in_node from t where id = 10
  • @B: select id from t where in_node = $A or out_node = $A --select in neighbor ids (1,2,3,4,10)
  • @C: select in_node as that_node from t where id in (@B) -- ('A', 'B', 'C')      
       union all       
       select out_node as that_node from t where id in (@B)  --('A','B','Z','D')
  • (count distinct @C)  --5 now we can subtract two ('A' and 'Z' nods) to get the number of neighbors from in_node side

    要计算in_weight,我们应该从t在(2,3,4)中的id中选择sum(weight)。我们不应该为id = 1计算重量,因为

    select in_node from t where id = 1     
    union all      
    select out_node from t where id = 1      
    --'A','B'
    

    产生与

    相同的集合
    select in_node from t where id = 2      
    union all     
    select out_node from t where id = 2     
    --'A','B'
    

    同时

    select weight from t where id = 2
    

    小于

    select weight from t where id = 1
    

是否可以从Oracle PL SQL中进行这种选择,还是仅将结果集传递给Java更容易?


此表代表图形。顶点为in_nodeout_nodeid对应于边缘,weight对应于边缘的权重。我想找到所有相邻的边缘及其权重。如果有多个连接两个顶点的边,则仅应考虑最浅的边。

因此,“ in_total”对应于“不同的”相邻边缘的权重的总和。 “截然不同”是指连接两个给定顶点的最亮边。

我正在模拟整个图形的最大移动距离。有时会多次报告A-B路线。在这种情况下,我只想选择一条重量最轻的A-B路线。

邻居总数超过邻居总数是解决我的问题的有用指标。

1 个答案:

答案 0 :(得分:2)

这可以使用分层查询来获取相邻节点,然后进行相应的求和:

WITH t AS (SELECT 1 ID, 'A' in_node, 'B' out_node, 17 weight FROM dual UNION ALL
           SELECT 2 ID, 'B' in_node, 'A' out_node, 4 weight FROM dual UNION ALL
           SELECT 3 ID, 'C' in_node, 'A' out_node, 5 weight FROM dual UNION ALL
           SELECT 4 ID, 'A' in_node, 'D' out_node, 6 weight FROM dual UNION ALL
           SELECT 5 ID, 'C' in_node, 'G' out_node, 33 weight FROM dual UNION ALL
           SELECT 6 ID, 'X' in_node, 'Z' out_node, 12 weight FROM dual UNION ALL
           SELECT 7 ID, 'Z' in_node, 'Y' out_node, 15 weight FROM dual UNION ALL
           SELECT 8 ID, 'X' in_node, 'Y' out_node, 42 weight FROM dual UNION ALL
           SELECT 9 ID, 'K' in_node, 'M' out_node, 66 weight FROM dual UNION ALL
           SELECT 10 ID, 'A' in_node, 'Z' out_node, 20 weight FROM dual),
   res AS (SELECT ID,
                  in_node,
                  out_node,
                  weight,
                  MAX(CASE WHEN ID = connect_by_root(ID) THEN in_node END) OVER () orig_in_node,
                  MAX(CASE WHEN ID = connect_by_root(ID) THEN out_node END) OVER () orig_out_node,
                  MAX(CASE WHEN ID = connect_by_root(ID) THEN ID END) OVER () orig_id,
                  CASE WHEN MAX(CASE WHEN ID = connect_by_root(ID) THEN in_node END) OVER () IN (in_node, out_node) THEN 'in'
                       ELSE 'out'
                  END direction_from_orig_node,
                  LEAST(in_node, out_node) node1,
                  GREATEST(in_node, out_node) node2,
                  row_number() OVER (PARTITION BY LEAST(in_node, out_node), GREATEST(in_node, out_node) ORDER BY weight) rn
           FROM   t
           START WITH ID = 10
           CONNECT BY NOCYCLE (PRIOR out_node IN (in_node, out_node)
                              OR PRIOR in_node IN (in_node, out_node))
                              AND LEVEL <= 2)
SELECT orig_id,
       COUNT(DISTINCT CASE WHEN direction_from_orig_node = 'in' THEN node1||'~'||node2 END) in_count,
       nvl(SUM(CASE WHEN direction_from_orig_node = 'in' THEN weight END), 0) in_sum,
       COUNT(DISTINCT CASE WHEN direction_from_orig_node = 'out' THEN node1||'~'||node2 END) out_count,
       nvl(SUM(CASE WHEN direction_from_orig_node = 'out' THEN weight END), 0) out_sum
FROM   res
WHERE  rn = 1
AND    ID != orig_id
GROUP BY orig_id;

给出:

   ORIG_ID   IN_COUNT     IN_SUM  OUT_COUNT    OUT_SUM
---------- ---------- ---------- ---------- ----------
        10          3         15          2         27

如果起始ID = 5,则得到:

   ORIG_ID   IN_COUNT     IN_SUM  OUT_COUNT    OUT_SUM
---------- ---------- ---------- ---------- ----------
         5          1          5          0          0