2

在我的PostgreSQL 9.4數據庫,我有一個表fieldsname列與唯一值。SQL約束檢查值是否不存在於另一個表

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

例如,如果fields.name包含值「顏色」「長度」,我需要防止fields_new.name從含有「顏色」「長度」值。換句話說,我需要提供兩個表中的name列在它們之間沒有任何重複值。約束應該是雙向的。

+1

請編輯您的問題,並提供示例數據和可接受和不可接受的記錄。 –

+0

我不明白你爲什麼想要兩張桌子。你不能只擴展當前表來迎合其他屬性嗎? –

+0

我應該如何編輯問題?完整的表格結構不會有幫助,因爲這個問題或多或少是純粹的理論。 – onerror

回答

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; 

另外,當然,在fields_new(name)以及對fields(name)一個UNIQUEPRIMARY KEY約束。

相關:

強制兩種方式

你可以走得更遠一步,反映在2臺以上CHECK約束。當兩筆交易同時寫入兩張表時,仍然無法保證抵禦惡劣的競爭條件。

您可以使用觸發器手動維護「物化視圖」:兩個name列的聯合。在那裏添加一個UNIQUE約束。並不像單個表上的相同約束一樣堅實:在同一時間寫入兩個表可能存在競爭條件。但是可能發生的最糟糕的情況是死鎖,迫使交易回滾。如果所有寫入操作級聯到「實體化視圖」,則永久性違規不會蔓延。

在這個相關答案 「陰暗面」 類似:

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

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');