Postgres父级-子网络ID

问题描述

我需要计算相互依赖的对象的网络。对于每个E-C链接,我都需要另外一列,即这些对象所属的“唯一网络ID”。例如金融业,贷款与他们融资的对象相关联。

create table ec (
    e varchar(10),c varchar(10)
);

insert into ec values ('E1','C1');
insert into ec values ('E1','C2');
insert into ec values ('E1','C3');
insert into ec values ('E2','C3');
insert into ec values ('E3','C4');
insert into ec values ('E4','C5');
insert into ec values ('E4','C6');

输出应为以下之一:

+--------+--------+------------+
| EXP_ID | CRM_ID | NETWORK_ID |
+--------+--------+------------+
| E1     | C1     |          1 |
| E1     | C2     |          1 |
| E1     | C3     |          1 |
| E2     | C3     |          1 |
| E3     | C3     |          1 |
| E3     | C4     |          1 |
| E4     | C5     |          2 |
| E4     | C6     |          2 |
+--------+--------+------------+

或者:

+----+------------+
| ID | NETWORK_ID |
+----+------------+
| E1 |          1 |
| E1 |          1 |
| E1 |          1 |
| E2 |          1 |
| E3 |          1 |
| E3 |          1 |
| C1 |          1 |
| C2 |          1 |
| C3 |          1 |
| C3 |          1 |
| C3 |          1 |
| C4 |          1 |
| E4 |          2 |
| C5 |          2 |
| C6 |          2 |
+----+------------+

视觉连接可以这样看:

enter image description here

我一直在研究递归查询,但是我不确定这是否是正确的方法。 那么,递归查询是实现此目标的一种方法吗?我应该再考虑一下吗?还是需要图分析之类的东西?

解决方法

是的,递归查询可以实现这一点。这是一个概念证明,它确实为每个边缘计算了可到达边缘(即网络中的所有边缘)的传递集,并通过为边缘赋予的ID进行键运算,然后以最小的(ID)边缘作为代表网络,每个边缘:

WITH RECURSIVE eci AS (
  SELECT row_number() OVER () AS id,* FROM ec
),networks AS (
  SELECT * FROM eci
UNION
  SELECT LEAST(eci.id,n.id),eci.e,eci.c FROM eci JOIN networks n ON n.e = eci.e OR n.c = eci.c
)
SELECT min(id),ec.e,ec.c FROM ec JOIN networks USING (e,c) GROUP BY e,c;

dbfiddle demo

免责声明:我怀疑这是有效的。我已经尝试过但在递归期间无法修剪networks

,

我一直在尝试不同的想法,以减少大型网络中所需的工作。

我在玩数组,我被Recurives CTE所阻止,不允许对递归表达式进行聚合或多重引用(不将CTE自身加入)

我目前的“最佳”尝试是通过递归组合集合来解决该问题。如果满足以下条件,则将一个集合合并到另一个集合中:

  • 这两个集合共享一个成员c
  • “其他”集的标识符为“下”

我希望这意味着最坏的情况是二进制模式。 1024行最多需要10个的递归深度(1024个集合变为512个,变为256个,等等)

我之所以这样想,是因为@Bergi的分析器最坏的情况是1024个节点的递归深度为1023。

但是,相反,我的方法最终需要为每个迭代花费更多的精力(我认为)。我不知道哪种方法在较大的数据集上表现更好。

  • 我不是说Bergi的病很严重
  • 我并不是说我的更好
  • 我只是说说他们与众不同

https://dbfiddle.uk/?rdbms=postgres_12&fiddle=b77940437835bb839ea3c92b05b686e9

WITH RECURSIVE
  groups AS
(
  SELECT
    e,c,DENSE_RANK() OVER (ORDER BY e) AS group_id,0                              AS search_depth,COUNT(*) OVER ()               AS total_changes
  FROM
    ec

  UNION ALL

  SELECT
    e,new_group_id               AS group_id,search_depth + 1           AS search_depth,SUM(has_changed) OVER ()   AS total_changes
  FROM
  (
    SELECT
      e,group_id,search_depth,new_group_id,CASE WHEN group_id = new_group_id THEN 0 ELSE 1 END  AS has_changed
    FROM
    (
      SELECT
        e,MIN(new_group_id) OVER (PARTITION BY group_id) AS new_group_id
      FROM
      (
        SELECT
          e,MIN(group_id) OVER (PARTITION BY c) AS new_group_id
        FROM
          groups
        WHERE
          total_changes > 0
      )
        combine_by_c
    )
      combine_by_group
  )
    tally_changes
)
SELECT * FROM groups WHERE total_changes = 0

编辑:

再进行两次尝试,确定不再可能增长的组,并将它们从进一步的迭代中排除。

根据数据的配置文件,此操作可能比保存的工作要花费更多的精力(大多数组需要类似的递归深度),或者可能会有所帮助(需要较大的递归深度变化)...

https://dbfiddle.uk/?rdbms=postgres_12&fiddle=0710b63cb39fe92e08156a486c5f2216