【问题标题】:SQL constraint to check whether value doesn't exist in another table用于检查值是否在另一个表中不存在的 SQL 约束
【发布时间】:2016-04-23 15:38:33
【问题描述】:

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

我正在创建一个具有类似结构的新表fields_new(此处不重要)和一个列name。我需要一种方法来限制 name 值插入到 fields_new 中不出现在 fields.name 中。

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

【问题讨论】:

  • 请编辑您的问题并提供样本数据和可接受和不可接受的记录。
  • 我不明白你为什么想要两张桌子。你不能扩展当前表来满足额外的属性吗?
  • 我应该如何编辑问题?完整的表格结构无济于事,因为这个问题或多或少是纯粹的理论问题。
  • 两张表,由于某种原因,我不能这样做。真正的db结构很复杂,第二张表是第一张表的非同步补充
  • 您仍然需要更具体。如果在fields 中插入/更新了已经添加到fields_new 的值(然后是合法的)怎么办?或者,在输入时限制新条目就足够了吗?这意味着在那之后的任何时候都可能发生违规行为。同样重要的是:您的 Postgres 版本?

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


【解决方案1】:

仅对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;

当然,还有一个UNIQUEPRIMARY KEY 约束fields_new(name) 以及fields(name)

相关:

双向执行

您可以更进一步,将上述CHECK 约束镜像到第二张表上。当两个事务同时写入两个表时,仍然不能保证不会出现令人讨厌的竞争条件。

或者您可以使用触发器手动维护“物化视图”:两个 name 列的联合。在此处添加 UNIQUE 约束。不像单个表上的相同约束那样坚如磐石:同时写入两个表可能存在竞争条件。但可能发生的最坏情况是死锁迫使事务回滚。如果所有写入操作都级联到“物化视图”,则不会出现永久性违规。

类似于此相关答案中的“黑暗面”:

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

【讨论】:

    【解决方案2】:

    我有一个类似的问题,我想维护每个公司的项目列表,以及所有公司的全局列表。如果公司编号为 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');
    

    【讨论】:

      猜你喜欢
      • 2018-08-10
      • 2020-12-03
      • 1970-01-01
      • 2022-12-20
      • 2011-05-22
      • 1970-01-01
      • 1970-01-01
      • 2021-10-27
      • 2020-12-12
      相关资源
      最近更新 更多