【问题标题】:POSTGRESQL pl/pgsql trigger function returns null on insertPOSTGRESQL pl/pgsql 触发器函数在插入时返回 null
【发布时间】:2012-01-27 11:26:25
【问题描述】:

我有一个这样的 pg 表:

CREATE TABLE order_status_history (
order_id integer NOT NULL,
status character varying NOT NULL,
sequence integer DEFAULT 1 NOT NULL,
date_status timestamp with time zone DEFAULT now() NOT NULL,
record_state character varying(12) DEFAULT 'ACTIVE'::character varying NOT NULL
);

在 order_id 和序列上使用复合 PK。 本质上,对于每个单独的 order_id,状态列是一个从 1 开始的自动递增值。因此,如果 order_id 2 已经有两行,则序列将为 1 和 2,对于新行,序列应为 3。 我试图在插入之前使用触发器来实现此行为,但是当我尝试为新的 order_id 插入第一行时(即触发器不必在插入之前更改行),我得到了一个错误为PG。说它不能将 NULL 插入序列中。我看不到我的触发函数将如何返回 NULL,但我的 pl/sql 不是很好,所以我确定这很简单...下面的触发函数,谢谢。

DECLARE
    seq_no INTEGER;

BEGIN
    SELECT INTO seq_no MAX(sequence) FROM rar.order_status_history WHERE order_id = NEW.order_id;
    IF FOUND THEN 
        NEW.sequence := seq_no + 1;
    END IF;

    RETURN NEW;
END;

【问题讨论】:

  • 使用SELECT max() 获取新ID 是一个非常非常糟糕的主意。它不是交易安全的,您的解决方案将在多用户环境中创建错误的序列号。
  • @a_horse_with_no_name,很好,谢谢,有没有更好的方法来实现这一点?
  • 如果你想让那个事务安全,你必须独占锁定表。

标签: sql postgresql triggers insert plpgsql


【解决方案1】:

如果该 order_id 在 order_status_history 中没有任何内容,则 MAX(sequence) 将为空。使用 COALESCE(MAX(sequence),0) 使其默认为 1。

你可以简单地写:

NEW.sequence := (SELECT COALESCE(MAX(sequence),0) FROM /* etc.. */) + 1;

在执行此操作之前,您确实应该将订单行锁定为独占模式,以允许这与同时插入同一订单历史记录的多个交易一起使用。那就是:

SELECT 1 FROM orders WHERE orders.order_id = NEW.order_id FOR UPDATE;

【讨论】:

  • 谢谢,更有意义。一旦触发功能结束,我是否需要释放锁或者是隐含的?有没有什么好的网络资源可以让我更好地编写这些函数?我发现很难找到一个好的、全面的教程,并从我找到的内容中拼凑起来。
  • 事务结束时锁会自动释放。尽早释放它没有意义,因为其他事务将无法看到由锁保护的更改。
【解决方案2】:

max(sequence) 始终是FOUND,但如果没有数据,它可能是NULL。在这种情况下,您的NEW.sequence := seq_no + 1 仍然是NULLIF seq_no IS NOT NULL 听起来更合适。

【讨论】:

    猜你喜欢
    • 2014-03-12
    • 1970-01-01
    • 1970-01-01
    • 2012-08-05
    • 1970-01-01
    • 2017-04-16
    • 2012-08-16
    • 2013-06-19
    • 1970-01-01
    相关资源
    最近更新 更多