1

我知道如何创建一个触发器来检查一组列是否对一个表有一个且只有一个 NON NULL,但我想重用代码,因为我将有一些具有相同要求的其他表。有什么建议吗?我在想可能是一个触发器,它将要检查的列的名称和表名传递给存储过程,其余的由函数完成,但我不确定如何实现它。

编辑:我试过

DROP tAble if exists t;
create table t(

a integer,
b integer,
c integer,
CONSTRAINT enforce_only1FK CHECK ((a <> NULL)::integer +(b <> NULL)::integer+(c <>NULL)::integer  = 1)
);
INSERT into t VALUES (4,NULL,6);

它不应该允许插入,但它确实......我做错了什么?

编辑2:有趣......如果我写它就可以了

DROP tAble if exists t;
create table t(

a integer,
b integer,
c integer,
CONSTRAINT enforce_only1FK CHECK ((a NOT NULL)::integer +(b NOT NULL)::integer+(c NOT NULL)::integer  = 1)
);
INSERT into t VALUES (4,NULL,6);
4

3 回答 3

1

一个触发器,用于检查一组列是否有一个且只有一个 NON NULL 用于一个表

这将是表级检查约束而不是触发器的情况。前 3 列的约束示例:

CREATE TABLE tablename (
 a int, 
 b int,
 c int,
 d text,
 CHECK ((a is not null and b is null and c is null)
  OR (a is null and b is not null and c is null)
  OR (a is null and b is null and c is not null))
);

或者以更精细的形式使用函数:

CREATE FUNCTION count_notnull(variadic arr int[]) returns int as
$$
 select sum(case when $1[i] is null then 0 else 1 end)::int
     from generate_subscripts($1,1) a(i);
$$ language sql immutable;

CREATE TABLE tablename (
 a int, 
 b int,
 c int,
 d text,
 CHECK (count_notnull(a,b,c)=1)
);

当约束中涉及许多列时,第二种形式看起来更好,但它要求它们都是相同的类型。

于 2013-06-20T10:13:23.453 回答
1

这不是触发器的情况。只是一个检查约束:

create table t (
    a integer,
    b text,
    c boolean
    check ((
        (a is not null)::integer
        + (b is not null)::integer
        + (c is not null)::integer
        ) = 1)
);]

而不是检查每个可能的组合,只需使用布尔转换为整数并对结果求和。

insert into t (a, b, c) values
(1, 'a', true);
ERROR:  new row for relation "t" violates check constraint "t_check"
DETAIL:  Failing row contains (1, a, t).

insert into t (a, b, c) values
(null, 'b', false);
ERROR:  new row for relation "t" violates check constraint "t_check"
DETAIL:  Failing row contains (null, b, f).

insert into t (a, b, c) values
(2, null, null);
INSERT 0 1

insert into t (a, b, c) values
(null, null, null);
ERROR:  new row for relation "t" violates check constraint "t_check"
DETAIL:  Failing row contains (null, null, null).
于 2013-06-20T11:36:15.920 回答
0

我不确定,以下技巧有多少干净或丑陋,但它有效:

postgres=# 创建表 foo1(a int);
创建表

postgres=# 在 foo1((1)) 上创建唯一索引,其中 a 为空;
创建索引

postgres=# 插入 foo1 值(空);
插入 0 1

postgres=# 插入 foo1 值(空);
错误:重复键值违反唯一约束“foo1_expr_idx”
详细信息:键 ((1))=(1) 已存在。
postgres=#

触发器注意事项:PL/pgSQL 不是编写通用触发器的好语言。您应该使用 PLperl 或 PLpython。下一个 - 触发器不是确保唯一性的好工具 - 你应该非常小心 - 存在竞争条件或高锁定的风险 - 它需要一些经验才能很好地工作。所以更好的方法是尽可能使用索引。

于 2013-06-20T09:14:26.023 回答