如何使PostgreSQL在从另一个表中删除时向表中插入一行?

Ben*_*Jin 11 postgresql triggers plpgsql

我们有一个应用程序,它将根据用户请求从表中删除一行.我无法更改应用程序代码.但是,我想根据要删除的行的信息,使用来自其他几个表的信息将一行插入另一个表(有点像日志日志).

我如何在PostgreSQL中实现这一目标?

Erw*_*ter 14

写一个触发函数.像这样的东西:

CREATE OR REPLACE FUNCTION trg_backup_row()
  RETURNS trigger AS
$BODY$
BEGIN

INSERT INTO other_tbl
SELECT (OLD).*, t.other_col                -- all columns of from old table
-- SELECT OLD.col1, OLD.col2, t.other_col  -- alternative: some cols from old tbl
FROM   third_tbl t
WHERE  t.col = OLD.col  -- link to third table with info from deleted row
AND    <unique_condition_to_avoid_multiple_rows_if_needed>;

RETURN NULL;

END;
$BODY$
  LANGUAGE plpgsql VOLATILE;
Run Code Online (Sandbox Code Playgroud)

并触发ON DELETE.像这样:

CREATE TRIGGER delaft
  AFTER DELETE
  ON tbl
  FOR EACH ROW
  EXECUTE PROCEDURE trg_backup_row();
Run Code Online (Sandbox Code Playgroud)

关键要素

  • 最好使之成为触发AFTER DELETEFOR EACH ROW.

  • 要返回旧表中的所有列,请使用语法(OLD).*.请参阅有关访问复合类型的手册.或者OLD.*也是有效的语法,因为隐式地OLD添加到FROM子句中.但是,对于VALUES表达式,它必须是(OLD).*.喜欢:

    INSERT INTO other_tbl
    VALUES((OLD).*, some_variable)
    
    Run Code Online (Sandbox Code Playgroud)
  • 您可以包含我演示的任何其他表中的值.只需确保获得一行,或者创建多个条目.

  • 当触发器触发AFTER事件时,函数可以RETURN NULL.


关于可见性

回应@ couling的注意评论.

虽然外键可以声明为DEFERRED,但这只会推迟完整性检查,而不是删除本身.那些在一个手头之前触发器执行删除的行ON DELETE CASCADE外键将不可见任何更在这个时间AFTER DELETE触发器被调用.(这一切都发生在一个交易明显.这些细节没有关系的其他交易,这将看到的效果全或无的.请参阅手册,了解更多关于MVCC模型和事务隔离.)

因此,如果您希望在您的行中包含依赖于行的值,请INSERT确保删除这些行之前调用此触发器.

你可能不得不做这个触发器BEFORE DELETE.

或者它可能意味着你必须相应地订购你的触发器,显然BEFORE触发器触发前AFTER.并且在同一级别的触发器按字母顺序执行.

但是,只要我在这里非常精确,我可能还会补充说,对其他BEFORE触发器中的行(或依赖行)所做的更改也只有之前调用它们时才可见.

我的建议,使其成为一个AFTER触发是因为它是不容易的并发症和便宜,如果其他触发可能会取消(回滚)的DELETE中途操作-只要没有上述适用.