postgres 触发器创建索引:BEFORE INSERT ON 隐藏一行

Posted

技术标签:

【中文标题】postgres 触发器创建索引:BEFORE INSERT ON 隐藏一行【英文标题】:postgres trigger creates index: BEFORE INSERT ON hides one row 【发布时间】:2017-05-22 09:46:49 【问题描述】:

我有一个调用函数的触发器AFTER INSERT ON mytable

CREATE OR REPLACE FUNCTION myfunction() RETURNS trigger AS
$BODY$
    DECLARE
        index TEXT;
    BEGIN
        index := 'myIndex_' || NEW.id2::text;
        IF to_regclass(index::cstring) IS NULL THEN
            EXECUTE 'CREATE INDEX ' || index || ' ON mytable(id) WITH (FILLFACTOR=100) WHERE id2=' || NEW.id2|| ';';
            RAISE NOTICE 'Created new index %',index;
        END IF;
        RETURN NULL;
    END;
$BODY$
LANGUAGE plpgsql VOLATILE
SECURITY DEFINER
COST 100;
ALTER FUNCTION myfunction()
    OWNER TO theadmin;

这非常有效。对于每个不同的id2,我都会创建一个索引。大大加快了相关查询的速度。

如上所述,我触发了这个AFTER INSERT ON。然而,在这样做之前,我将触发器设置为BEFORE INSERT ON。这个函数做了一些奇怪的事情。 (是的,我已将RETURN NULL 更改为RETURN NEW

插入新行insert into mytable VALUES(1391, 868, 0.5, 0.5); 创建对应的索引myIndex_868 在进行选择时插入的行没有出现在 mytable 中:( 尝试插入同一行会导致ERROR: duplicate key value violates unique constraint "mytable_pkey",因为当然是DETAIL: Key (id, id2)=(1391, 868) already exists. 为相同的 id2 插入其他行按预期工作:) DELETE FROM mytable WHERE id = 1391 and id2 = 868 什么都不做 DROP INDEX myIndex_868; 删除索引。突然间,从未出现在表格中的初始行突然出现了!

为什么BEFORE INSERT ON 的行为如此不同?这是 postgres 9.4 中的错误还是我忽略了什么?

为了完整起见:

CREATE TRIGGER mytrigger
AFTER INSERT ON mytable
FOR EACH ROW EXECUTE PROCEDURE myfunction();

对比

CREATE TRIGGER mytrigger
BEFORE INSERT ON mytable
FOR EACH ROW EXECUTE PROCEDURE myfunction();

【问题讨论】:

【参考方案1】:

我认为这是 PostgreSQL 中的一个错误。我可以用 9.6 重现它。

很明显,该行不包含在索引中,因为它是在BEFORE 触发器中创建的,但在我看来,插入行时索引没有更新这一事实是一个错误。

我有written to pgsql-hackers 征求意见。

但除此之外,我看不出整个练习的意义所在。 比创建一个庞大的索引更好的是创建一个:

CREATE INDEX ON mytable(id2, id);

【讨论】:

这会覆盖我对 id2 的所有查询吗?SELECT * FROM mytable WHERE id2=$1 过去很慢,现在每个 WHERE 子句都有一个索引,速度很快。 为此,更简单的索引会更好:CREATE INDEX ON mytable(id2) 你确定吗?我的印象是每个 WHERE 子句都需要一个索引。此外,我正在按 id 对结果进行排序。 SELECT * FROM mytable WHERE id2=$1 ORDER BY id; 和索引大大加快了速度。 好吧,然后使用我在回答中给出的索引。试试看,你会发现它一样快。在查询计划和其他事情期间,这些索引会让您头疼。 谢谢!这很容易:)

以上是关于postgres 触发器创建索引:BEFORE INSERT ON 隐藏一行的主要内容,如果未能解决你的问题,请参考以下文章

创建或替换触发器 postgres

在 Postgres 上使用 pl/Python 创建触发器

如何列出为 postgres 中的表创建的索引

在 Postgres 上使用 sqlalchemy 创建部分唯一索引

如何在 Postgres 中的 JSON 字段上创建索引?

在 Postgres 中使用审计表为 NOTIFY/LISTEN 创建触发器是个好主意吗?