在 PostgreSQL 中验证 LTREE 层次结构

问题描述 投票:0回答:2

一般来说,我是层次结构的新手,尤其是 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 数据类型中找到这些缺失的关系?

postgresql hierarchy ltree
2个回答
0
投票

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

在线演示


0
投票

我在使用ltree扩展时也遇到了数据不一致的问题。 正如 Bergi 所说的那样,树不关心或检查您在此字段中输入的内容,并且可以使用触发器解决此问题。这很好 我经历了它并分享了我在这个故事中的经验:Ensuring data consistency when using PostgreSQL ltree extension。希望它能帮助你。 这是一个很好的解决方案,但首先您需要使数据处于一致状态。

© www.soinside.com 2019 - 2024. All rights reserved.