将插入重定向到分区表时具有多个触发事件

时间:2019-01-17 13:59:26

标签: postgresql database-trigger database-partitioning postgresql-9.6

我正在尝试为PostgreSQL中某些分区表的主表的插入和更新事件设置触发器。每次在主表中进行插入时,插入触发事件都会将其重定向到正确的分区表中。因此,由于我也不想填充主表,因此需要从此函数调用返回NULL。如果主表收到更新事件,它将在对表进行更改之前更新时间戳。问题在于,永远不会触发更新触发器。我正在使用PostgreSQL 9.6版。

我尝试将触发器函数合并为一个,并将调用的触发器过程也合并为一个,但是结果是相同的。仅当我从插入触发函数(填充主表)返回NEW时,或者完全注释掉插入触发函数时,才会触发更新触发。

DROP SCHEMA IF EXISTS test CASCADE;
CREATE SCHEMA test;
SET SCHEMA 'test';

CREATE TYPE test_type AS ENUM ('unit', 'performance');

CREATE TABLE test (
    type test_type NOT NULL,
    score INTEGER NOT NULL CHECK (score > 0),
    id SERIAL PRIMARY KEY,
    updated_at TIMESTAMP DEFAULT current_timestamp
);

CREATE TABLE performance_test (
    CHECK (type = 'performance')
) INHERITS (test);

CREATE FUNCTION insert_test()
RETURNS trigger AS
$$
BEGIN
    INSERT INTO performance_test VALUES (NEW.*);
    RETURN NULL;
END;
$$ LANGUAGE plpgsql;

CREATE FUNCTION update_timestamp()
RETURNS trigger AS
$$
BEGIN
    RAISE NOTICE 'This is never reached.';

    UPDATE performance_test
    SET updated_at = current_timestamp
    WHERE id = NEW.id;

    RETURN NULL;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER test_insertion BEFORE INSERT ON test
    FOR EACH ROW EXECUTE PROCEDURE insert_test();

CREATE TRIGGER test_update BEFORE UPDATE ON test
    FOR EACH ROW EXECUTE PROCEDURE update_timestamp();

---------------------------------------------------------------------------

INSERT INTO test VALUES ('performance', 10);

SELECT * FROM performance_test;

UPDATE test SET score = 20 WHERE id = 1;

SELECT * FROM performance_test;

我不确定用这种方法是否可以实现我想要的,所以我在这里寻求任何建议。预先感谢!

/汉普斯

1 个答案:

答案 0 :(得分:0)

行触发器必须在单个分区上定义,而不是在分区表上定义。参见https://www.postgresql.org/docs/10/ddl-partitioning.html#DDL-PARTITIONING-DECLARATIVE-LIMITATIONS

我不知道为什么9.6的文档中没有提及

工作更新触发器:

CREATE FUNCTION update_timestamp()
RETURNS trigger AS
$$
BEGIN
  NEW.updated_at = now();
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER test_update BEFORE UPDATE ON performance_test
    FOR EACH ROW EXECUTE PROCEDURE update_timestamp();

如果您执行UPDATE test SET score = 30, updated_at=DEFAULT;UPDATE test SET score = 30, updated_at=current_timestamp;,则可能不需要更新触发器。

分区不是免费的午餐,因为它对行为和性能都没有明显的影响,正如您所注意到的那样,触发器的行为并不符合您的预期。如果您输入有误,很容易导致查询失败,甚至是不良数据。

如果您确实确定需要它,则应确保您详细了解它,否则,我建议您避免使用它,可以通过确保表统计信息是最新的来解决慢查询的大多数问题,使用正确的索引,优化查询,更改Postgres配置或添加更多硬件。