SQL 检查值是否在另一个 table 中不存在的约束
SQL constraint to check whether value doesn't exist in another table
在我的 PostgreSQL 9.4 数据库中,我有一个 table fields
列 name
具有唯一值。
我正在创建一个新的 table fields_new
,它具有类似的结构(此处不重要)和一个列 name
。我需要一种方法来限制要插入到 fields_new
中的 name
值不出现在 fields.name
.
中
例如,如果 fields.name
包含值 'color' 和 'length',我需要防止 fields_new.name
包含 'color' 或 'length' 值。因此,换句话说,我需要提供两个 table 中的 name
列之间没有任何重复值。约束应该是双向的。
仅对 fields_new
中的新条目实施约束
CHECK
约束应该是 immutable,这通常排除了对其他 table 的任何引用,这天生不免疫table。
允许一些回旋余地(尤其是时间函数)STABLE
函数是可以容忍的。显然,这在具有并发写访问的数据库中不能完全可靠。如果引用的 table 中的行发生更改,则它们可能违反了约束。
声明 您的约束的无效性质,使其成为 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 = );
$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)
的约束。
相关:
- CONSTRAINT to check values from a remotely related table (via join etc.)
- Function to update a status flag for validity of other column?
- Trigger vs. check constraint
双向执行
您可以更进一步,在第 2 个 table 上反映上述 CHECK
约束。当两个事务同时写入两个 table 时,仍然无法保证不会出现令人讨厌的竞争条件。
或 您可以使用触发器手动维护“实体化视图”:两个 name
列的联合。在那里添加一个 UNIQUE
约束。不像对单个 table 的相同约束那样坚如磐石:可能存在同时写入两个 table 的竞争条件。但可能发生的最坏情况是死锁迫使交易回滚。如果所有写操作都级联到“物化视图”,则不会出现永久性违规。
类似于此相关答案中的“阴暗面”:
- Can PostgreSQL have a uniqueness constraint on array elements?
只是您需要 INSERT
/ UPDATE
/ DELETE
两个 table 的触发器。
我有一个类似的问题,我想维护每个公司的项目列表,以及所有公司的全局列表。如果公司编号为 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 <> 0
and b.company = 0
and b.name_key = );
$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');
在我的 PostgreSQL 9.4 数据库中,我有一个 table fields
列 name
具有唯一值。
我正在创建一个新的 table fields_new
,它具有类似的结构(此处不重要)和一个列 name
。我需要一种方法来限制要插入到 fields_new
中的 name
值不出现在 fields.name
.
例如,如果 fields.name
包含值 'color' 和 'length',我需要防止 fields_new.name
包含 'color' 或 'length' 值。因此,换句话说,我需要提供两个 table 中的 name
列之间没有任何重复值。约束应该是双向的。
仅对 fields_new
中的新条目实施约束
CHECK
约束应该是 immutable,这通常排除了对其他 table 的任何引用,这天生不免疫table。
允许一些回旋余地(尤其是时间函数)STABLE
函数是可以容忍的。显然,这在具有并发写访问的数据库中不能完全可靠。如果引用的 table 中的行发生更改,则它们可能违反了约束。
声明 您的约束的无效性质,使其成为 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 = );
$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)
的约束。
相关:
- CONSTRAINT to check values from a remotely related table (via join etc.)
- Function to update a status flag for validity of other column?
- Trigger vs. check constraint
双向执行
您可以更进一步,在第 2 个 table 上反映上述 CHECK
约束。当两个事务同时写入两个 table 时,仍然无法保证不会出现令人讨厌的竞争条件。
或 您可以使用触发器手动维护“实体化视图”:两个 name
列的联合。在那里添加一个 UNIQUE
约束。不像对单个 table 的相同约束那样坚如磐石:可能存在同时写入两个 table 的竞争条件。但可能发生的最坏情况是死锁迫使交易回滚。如果所有写操作都级联到“物化视图”,则不会出现永久性违规。
类似于此相关答案中的“阴暗面”:
- Can PostgreSQL have a uniqueness constraint on array elements?
只是您需要 INSERT
/ UPDATE
/ DELETE
两个 table 的触发器。
我有一个类似的问题,我想维护每个公司的项目列表,以及所有公司的全局列表。如果公司编号为 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 <> 0
and b.company = 0
and b.name_key = );
$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');