[英]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)
的UNIQUE
或PRIMARY 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.