簡體   English   中英

用於檢查另一個表中是否不存在值的SQL約束

[英]SQL constraint to check whether value doesn't exist in another table

在我的PostgreSQL 9.4數據庫中,我有一個表fields ,其列name具有唯一值。

我正在創建一個具有類似結構的新表fields_new (這里不重要)和列name 我需要一種方法來約束要插入fields_new name值,而不是出現在fields.name

例如,如果fields.name包含值'color''length' ,我需要阻止fields_new.name包含'color''length'值。 因此,換句話說,我需要提供兩個表中的name列之間沒有任何重復值。 約束應該是雙向的。

僅對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;

當然,還有對fields_new(name)以及fields(name)UNIQUEPRIMARY KEY約束。

有關:

實現兩種方式

您可以更進一步,並在第二個表上鏡像上面的CHECK約束。 當兩個事務同時寫入兩個表時,仍然無法保證惡劣的競爭條件。

或者您可以使用觸發器手動維護“物化視圖”:兩個name列的並集。 在那里添加一個UNIQUE約束。 不像單個表上的相同約束那樣堅如磐石:可能存在同時寫入兩個表的競爭條件。 但可能發生的最糟糕的事情是迫使交易回滾的僵局。 如果所有寫入操作都級聯到“物化視圖”,則不會發生永久性違規。

與此相關答案中的“黑暗面”相似:

只是你需要兩個表上的INSERT / UPDATE / DELETE觸發器。

我遇到了類似的問題,我想維護每個公司的項目清單,以及所有公司的全球清單。 如果公司編號為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');

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM