用于检查另一个表中是否不存在值的SQL约束

时间:2016-01-18 12:47:50

标签: sql postgresql database-design constraints unique-constraint

在我的PostgreSQL 9.4 数据库中,我有一个表fields,其列name具有唯一值。

我正在创建一个具有类似结构的新表fields_new(此处不重要)和列name。我需要一种约束name值的方法,以便fields_new中不会出现fields.name

例如,如果fields.name包含值'color''length',我需要阻止fields_new.name包含'color''length'值。因此,换句话说,我需要提供两个表中的name列之间没有任何重复值。约束应该是双向的。

2 个答案:

答案 0 :(得分:4)

仅对fields_new

中的新条目强制执行约束

CHECK约束应该是不可变的,它通常排除对其他表的任何类型的引用,这些表本质上不是不可变的。

为了允许一些余地(特别是对于时间函数),STABLE函数是可以容忍的。显然,在具有并发写访问权限的数据库中,这不可能完全可靠。如果引用表中的行发生更改,则可能违反约束。

通过 NOT VALID (Postgres 9.1+)声明约束的无效性质。这样Postgres也不会尝试在恢复过程中强制执行它(可能会失败)。详情如下:

仅对新行强制执行约束。

CREATE OR REPLACE FUNCTION f_fields_name_free(_name text)
  RETURNS bool AS
$func$
SELECT NOT EXISTS (SELECT 1 FROM fields WHERE name = $1);
$func$  LANGUAGE sql STABLE;

ALTER TABLE fields_new ADD CONSTRAINT fields_new_name_not_in_fields
CHECK (f_fields_name_free(name)) NOT VALID;

另外,当然,UNIQUE以及PRIMARY KEY上的fields_new(name)fields(name)约束。

相关:

实施两种方式

您可以更进一步,并在第二个表上镜像上面的CHECK约束。当两个事务同时写入两个表时,仍然无法保证恶劣的竞争条件。

您可以维持一个物化视图"手动触发:两个name列的并集。在那里添加UNIQUE约束。不像单个表上的相同约束那样坚如磐石:可能存在同时写入两个表的竞争条件。但可能发生的最糟糕的事情是迫使交易回滚的僵局。如果所有写入操作都级联到物化视图,则不会发生永久性违规。

类似于"黑暗面"在这个相关的答案:

只是您需要在两个表格上INSERT / UPDATE / DELETE触发。

答案 1 :(得分:0)

我遇到了类似的问题,我想维护每个公司的项目清单,以及所有公司的全球清单。如果公司编号为0,则将其视为全局,并且不能为使用该名称的任何公司插入新项目。以下脚本(基于上述解决方案)似乎有效:

drop table if exists blech;

CREATE TABLE blech (
        company int,
        name_key text,
        unique (company, name_key)
);

create or replace function f_foobar(new_company int, new_name_key text) returns bool as
$func$
select not exists (
        select 1 from blech b
        where $1 <> 0
        and b.company = 0
        and b.name_key = $2);
$func$ language sql stable;

alter table blech add constraint global_unique_name_key
check (f_foobar(company, name_key)) not valid;

insert into blech values(0,'GLOB1');
insert into blech values(0,'GLOB2');

-- should succeed:
insert into blech values(1,'LOCAL1');
insert into blech values(2,'LOCAL1');

-- should fail:
insert into blech values(1,'GLOB1');

-- should fail:
insert into blech values(0,'GLOB1');