在 tsrange 值上创建 GIST 索引时出现范围检查错误

Range check error when creating GIST index on tsrange value

修复范围绑定数据和创建要点 tsrange 索引导致异常。我猜 PostgreSQL 会看到旧版本的记录并在创建要点索引时考虑到它们。

您可以使用此脚本重现它:

BEGIN;
CREATE  TABLE _test_gist_range_index("from" timestamp(0) WITHOUT time zone, "till" timestamp(0) WITHOUT time zone);
--let's enter some invalid data
INSERT INTO _test_gist_range_index VALUES ('2021-01-02', '2021-01-01');
--let's fix the data
DELETE FROM _test_gist_range_index;
CREATE INDEX idx_range_idx_test2 ON _test_gist_range_index USING gist (tsrange("from", "till", '[]'));
COMMIT;

结果是:

SQL Error [22000]: ERROR: range lower bound must be less than or equal to range upper bound

db<>fiddle

我已经使用 db<>fiddle 在从 v9.5 到 v13 的所有版本的 PostgreSQL 上测试了这个。所有的结果都是一样的。

如果我们使用“UPDATE”修复数据,则会收到相同的错误。

有没有办法修复数据并在其上添加范围索引?也许有办法以某种方式从那些旧值中清除 table?..

编辑

似乎仅当数据更正语句(在我的示例中为DELETE)和CREATE INDEX语句在同一事务中时才会引发异常。如果我先DELETE然后COMMIT,再创建索引就成功了

这是按预期工作的,不是错误。

当您在 PostgreSQL table 中删除一行时,它实际上并没有被删除,而是被标记为不可见。同样,更新一行会创建该行的新版本,但旧版本会保留并标记为不可见。这就是 PostgreSQL 实现多版本的方式:并发事务仍然可以看到“不可见”的数据。最终,不可见的行被 VACUUM.

回收

现在,B 树或 GiST 索引包含 table 中每个行版本的一个条目,除非该行版本对任何人都不可见(dead ).这解释了为什么如果数据未形成有效范围,已删除的行仍会导致错误。

如果您 运行 在空闲数据库上以自动提交模式执行语句,则删除的行将失效,并且不必创建索引条目。