我正在为一个遗留系统工作一个新功能.该系统使用两个表来保存"文档"和保存文档之间的关系("relation").这个"关系"表创建了类似于树形 struct 的东西.

我们需要列出所有被分类为有效类型的文档,并且没有任何其他有效类型的文档作为其祖先.

文档(约5亿条记录在生产中)


id:   |  type
1   invalid_type
2   valid_type_a
3   invalid_type
4   valid_type_a
5   invalid_type
6   valid_type_b
7   invalid_type
8   valid_type_b
9   invalid_type
10  valid_type_a
11  valid_type_a
12  invalid_type
13  invalid_type
14  invalid_type
15  valid_type_b

关系(约5亿条记录在生产中)


relationId | parentDocumentId | childDocumentId
1       1       2
2       1       3
3       2       4
4       2       5
5       3       6
6       6       7
7       8       9
8       9       10
9       12      13
10      13      14
11      13      15

structure

在这些表中,我需要列出所有有效类型的文档,并且没有任何有效类型的祖先文档(在任何级别).

预期结果为:2、6、8、11、15

4是一个有效的文档,但有2作为它的父级.

10是一个有效的文件,但有8作为它的祖先.


虽然我最终可以改变这个 struct ,甚至可以将它迁移到另一个数据库(nosql...),现在的重点是利用现有的模式开发一个新的功能.

我一直在玩递归查询,但还不能把所有的东西放在一起. 我还可以在某种程度上 Select 和过滤记录,然后在代码中应用其余的规则.

任何指向任何方向的帮助或提示都非常感谢.


CREATE TABLE IF NOT EXISTS document
(
    id int not null,
    type varchar not null,
    CONSTRAINT document_pk PRIMARY KEY (id)
);

CREATE TABLE IF NOT EXISTS relation
(
    id int not null,
    parent_id int not null,
    child_id int not null,
    CONSTRAINT relation_pk PRIMARY KEY (id),
    CONSTRAINT parent_fk FOREIGN KEY (parent_id)
        REFERENCES document (id),
    CONSTRAINT child_fk FOREIGN KEY (child_id)
        REFERENCES document (id)
);


INSERT INTO document (id, type)
    VALUES 
        (1, 'invalid_type'), 
        (2, 'valid_type_a'), 
        (3, 'invalid_type'), 
        (4, 'valid_type_a'), 
        (5, 'invalid_type'), 
        (6, 'valid_type_b'), 
        (7, 'invalid_type'), 
        (8, 'valid_type_b'), 
        (9, 'invalid_type'), 
        (10, 'valid_type_a'), 
        (11, 'valid_type_a'), 
        (12, 'invalid_type'), 
        (13, 'invalid_type'), 
        (14, 'invalid_type'), 
        (15, 'valid_type_b');


INSERT INTO relation (id, parent_id, child_id)
    VALUES 
        (1, 1, 2), 
        (2, 1, 3),
        (3, 2, 4),
        (4, 3, 5),
        (5, 3, 6),
        (6, 6, 7),
        (7, 8, 9),
        (8, 9, 10),
        (9, 12, 13),
        (10, 13, 14),
                (11, 13, 15);

推荐答案

从每个候选 node ,你可以向上遍历树,并找到是否有一个有效的祖先.例如:

with recursive
n (id, lvl, cid, burned) as (
  select id, 1, id, false from document where type like 'v%'
 union all
  select n.id, n.lvl + 1, d.id, d.type like 'v%'
  from n
  join relation r on r.childDocumentId = n.cid and not n.burned
  join document d on d.id = r.parentDocumentId
)
select *
from (
  select distinct on (id) * from n order by id, lvl desc
) x
where not burned;

结果:

 id  lvl  cid  burned 
 --- ---- ---- ------ 
 2   2    1    f      
 6   3    1    f      
 8   1    8    f      
 11  1    11   f      
 15  3    12   f      

参见运行示例db<>fiddle.

Note:假设没有循环引用.

以下索引可以帮助提高性能:

create index ix1 on relation (childDocumentId, parentDocumentId);

create index ix2 on document (id);

Sql相关问答推荐

如何连接第二个表并将其内容输入到第一个表的单个字段中?

如何优化我的功能以减少花费的时间?

从依赖于其他表的值的XREF表中的值分组获得正确的计数?

对多个条件的SQL进行排名

在查询Oracle SQL中创建替代ID

动态组/转置

PATINDEX中与[A-Z]匹配(U除外)的正则表达式

从给定数据中查找下一个工作日期

如何判断小数点后千位是否不为0

SQL Server 查询 WHERE LIKE

DbUp for sqlserver 在 dbo 授权下为非 dbo 用户创建架构

递归 CTE 附加为行

JSON对象查询SQL服务器

在Snowflake中,如何将以逗号和连字符分隔的多个混合数值拆分成数字列表

所有列分组的简写?

Teradata 多个进程的最大进程结束时间捕获

使用日期和间隔作为键加入 Athena 上的表?

如何根据 Amazon Athena 中的多个列值删除重复行?

SQL 计数和过滤查询优化

在 sql 中合并系列以删除重复项