一般来说,我是层次结构的新手,尤其是 LTREE。当我将一列基于文本的层次结构转换并加载到 LTREE 列中时,我注意到一个格式不正确的字符串。
create table test_tree(id int, path ltree);
insert into test_tree values (1, '1');
insert into test_tree values (1, '1.1');
insert into test_tree values (1, '1.2.0'); --should be '1.2'
insert into test_tree values (1, '1.2.1');
insert into test_tree values (1, '1.2.2.0'); --should be '1.2.2'
insert into test_tree values (1, '1.2.2.1');
insert into test_tree values (1, '1.2.2.2');
这会导致一些意想不到的行为。
select path from test_tree where path <@ '1';
返回后代,即:
1
1.1
1.2.0
1.2.1
1.2.2.0
1.2.2.1
1.2.2.2
鉴于:
select path from test_tree where path @> '1.2.2.2';
只退货
1.2.2.2
我希望
<@ '1'
返回与@> '1.2.2.2'
一致的结果。这种情况下,祖先怎么能知道自己的后代,而后代却不知道自己的祖先呢?为什么<@ '1'
返回所有后代(貌似忽略了丢失的'1.2.2'
)但是@> '1.2.2.2'
没有返回祖先?
此外,如何在 LTREE 数据类型中找到这些缺失的关系?
ltree 运算符不关心你的表中有什么值。他们只比较两个 ltree 值。
'1' @> '1.2.2.2'
是真的,'1.2.2.2' @> '1.2.2.2'
是真的,'1.1' @> '1.2.2.2'
不是。
但是
SELECT
查询仅返回表中实际存在的行。 '1.2' @> '1.2.2.2'
和 '1.2.2' @> '1.2.2.2'
也是正确的,但是 这两个值在您的表中不存在,因此无法找到它们。 @>
/<@
运算符不构造新行。
要实际构建 ltree 值的所有可能祖先,而不仅仅是表中的那些,您可以使用
SELECT subpath(p, 0, generate_series(1, nlevel(p)))
(在线演示)
您似乎还假设了使用
ltree
列的隐式约束,即父值存在于表的同一列中。这是不可能的,在关系数据库中,行是相互独立的:ltree
值不是对另一行加上最后一个标签的引用,它实际上只是一个标签列表;表中的每一行都存储完整的标签路径。对列使用特定类型不能引入约束,您必须自己做 - 作为生成列的复杂外键,或使用触发器。
如何在 LTREE 数据类型中找到这些缺失的关系?
您可以使用在table(不在数据类型)中找到丢失的rows
SELECT path, array_agg(id) AS required_by
FROM (
SELECT id, subpath(path, 0, generate_series(1, nlevel(path)-1)) AS path
FROM test_tree
ORDER BY path, id
) AS all_parent_paths
WHERE NOT EXISTS (SELECT * FROM test_tree WHERE path = all_parent_paths.path)
GROUP BY path
(在线演示)
我在使用ltree扩展时也遇到了数据不一致的问题。 正如 Bergi 所说的那样,树不关心或检查您在此字段中输入的内容,并且可以使用触发器解决此问题。这很好 我经历了它并分享了我在这个故事中的经验:Ensuring data consistency when using PostgreSQL ltree extension。希望它能帮助你。 这是一个很好的解决方案,但首先您需要使数据处于一致状态。