关系计算的闭包表
邻接列表的限制
大多数树状结构首先使用邻接列表建模:每行存储 parent_id。这对于直接的父子关系很好,但对于需要遍历多代的查询来说很慢:"找到这个人的所有后代"需要递归查询,而"这两个人是什么关系?"需要从两端遍历。
闭包表结构
闭包表存储每对祖先-后代关系以及它们之间的距离:
CREATE TABLE family_closure (
ancestor_id TEXT,
descendant_id TEXT,
depth INTEGER,
PRIMARY KEY (ancestor_id, descendant_id)
);每个人与自己有一行(depth=0),与其父母有一行(depth=1),与其祖父母有一行(depth=2),依此类推。
查询模式
查找所有后代(任意深度):
SELECT descendant_id FROM family_closure
WHERE ancestor_id = :person_id AND depth > 0检查两人是否有亲属关系:
SELECT COUNT(*) > 0 FROM family_closure a
JOIN family_closure b ON a.ancestor_id = b.ancestor_id
WHERE a.descendant_id = :person_a AND b.descendant_id = :person_b维护闭包表
当添加一个新人时,必须插入包含其所有祖先的闭包行。这通常通过数据库触发器或应用层事务来处理:插入个人,然后从其父节点的现有闭包行中插入闭包行。
闭包表的读写权衡非常适合读多写少的家谱:每次添加都比邻接列表慢,但查询要快得多。