PostgreSQL中的“列中的空值违反了非空约束”错误

时间:2013-12-13 18:20:34

标签: sql postgresql database-design triggers common-table-expression

表示我遇到的问题的简化模式如下:

CREATE TABLE a (
   var1 text PRIMARY KEY
);

CREATE TABLE b (
   var1 text,
   var2 text PRIMARY KEY
);

CREATE TABLE c (
   var1 text,
   var2 text,
   var3 text PRIMARY KEY
);

在表c中,var1var2分别代表表ab中的键。

从这些值中,我们希望生成一个用于插入c的新元组的键。 我们通过创建一个调用函数的触发器来实现,如下所示:

CREATE TRIGGER key_Gen 
    BEFORE INSERT ON c 
    FOR EACH ROW 
    EXECUTE PROCEDURE key_Gen();

CREATE FUNCTION key_Gen() RETURNS trigger AS $key_Gen$
    BEGIN
        -- Check that new values are not null
        IF NEW.var1 IS NULL THEN
            RAISE EXCEPTION 'var1 cannot be null';
        END IF;

        IF NEW.var2 IS NULL THEN
            RAISE EXCEPTION 'var2 cannot be null';
        END IF;

        INSERT INTO a VALUES (NEW.var1);
        INSERT INTO b VALUES (NEW.var1 || NEW.var2);
        INSERT INTO c VALUES (NEW.var1 || NEW.var2 || NEW.var3);

        RETURN NEW;
    END;
$key_Gen$ LANGUAGE plpgsql;
  • 对于表格a,新密钥应为var1
  • 对于表格b,新密钥应为var1var2的串联。
  • 对于表格c,新密钥应为var1var2var3的串联。

假设我插入以下内容:

INSERT INTO c VALUES ( 'TEST', 'HAS', 'PASSED');

我收到以下错误消息:

null value in column "var2" violates not-null constraint

我尝试在触发器和功能中更改各种内容,例如将BEFORE INSERT更改为AFTER INSERT,但这不会影响其行为。
我也尝试过参考文档和其他各种例子,但我所看到的没有一个能解决这个问题的具体情况。

我的功能甚至可以完成吗?

2 个答案:

答案 0 :(得分:1)

你的触发器会像这样工作:

CREATE OR REPLACE FUNCTION key_gen()
  RETURNS trigger AS
$func$
BEGIN
-- Check that new values are not null
IF NEW.var1 IS NULL THEN
    RAISE EXCEPTION 'var1 cannot be null';
END IF;

IF NEW.var2 IS NULL THEN
    RAISE EXCEPTION 'var2 cannot be null';
END IF;

INSERT INTO a(var1)       VALUES (NEW.var1);
INSERT INTO b(var1, var2) VALUES (NEW.var1, NEW.var1 || NEW.var2);

NEW.var3 := (NEW.var1 || NEW.var2 || NEW.var3);

RETURN NEW;

END
$func$ LANGUAGE plpgsql;

两个问题:

  • 无限循环,因为您启动了另一个INSERT INTO c ...。只需指定NEW.var3

  • 您的INSERT语句通常应该包含持久语句的目标列表! 那么你就不会错过b的非法声明,你无意中指派var1并将pk列保留为NULL。

但我会不鼓励使用此设计

一个查询

删除触发器并将此查询与data-modifying CTEs一起使用。你要求的一切都是:

WITH val AS (
   SELECT 'TEST'::text   AS v1 -- insert values once
         ,'HAS'::text    AS v2
         ,'PASSED'::text AS v3
   )
, ins AS (
   SELECT v1             AS var1
         ,v1 || v2       AS var2
         ,v1 || v2 || v3 AS var3
   FROM   val
   WHERE  v1 IS NOT NULL
   AND    v2 IS NOT NULL
   AND    v3 IS NOT NULL
   )
, a AS (INSERT INTO a(var1)             SELECT var1             FROM ins)
, b AS (INSERT INTO b(var1, var2)       SELECT var1, var2       FROM ins)
        INSERT INTO c(var1, var2, var3) SELECT var1, var2, var3 FROM ins
RETURNING *;

最终的RETURNING子句是可选的。

此相关答案中的更多解释,链接和详细信息:
INSERT rows into multiple tables in a single query, selecting from an involved table

功能

如果需要,您可以轻松地将其包装到SQL函数中:

CREATE OR REPLACE function f_tripple_ins(v1 text, v2 text, v3 text)
  RETURNS void AS
$func$
WITH ins AS (
   SELECT v1             AS var1
         ,v1 || v2       AS var2
         ,v1 || v2 || v3 AS var3
   WHERE  v1 IS NOT NULL
   AND    v2 IS NOT NULL
   AND    v3 IS NOT NULL
   )
, a AS (INSERT INTO a(var1)             SELECT var1             FROM ins)
, b AS (INSERT INTO b(var1, var2)       SELECT var1, var2       FROM ins)
        INSERT INTO c(var1, var2, var3) SELECT var1, var2, var3 FROM ins
$func$ LANGUAGE sql;

适用于Postgres 9.2或更高版本。
在Postgres 9.1或更早版本中,您必须使用$1$2$3来引用SQL函数中的输入参数。

-> SQLfiddle demo.

答案 1 :(得分:0)

插入部件不应该如下所示:

INSERT INTO a VALUES (NEW.var1);
INSERT INTO b VALUES (NEW.var1, New.var1 || NEW.var2);
INSERT INTO c VALUES (NEW.var1, NEW.var2, NEW.var1 || NEW.var2 || NEW.var3);