2017-04-18 42 views
4

我有一個表在ETL例程中一次填充一列。 強制性列(其是外鍵)首先被設置,並在一次,所以表中的初始狀態爲:什麼時候運行NOT NULL約束,並且能等到事務要提交?

key | fkey | a 
-------|--------|------- 
1  | 1  | null 

處理的值後,我將它們插入使用SQL鍊金PostgreSQL的方言一個簡單的UPSERT:

upsert = sqlalchemy.sql.text(""" 
    INSERT INTO table 
     (key, a) 
    VALUES (:key, :a) 
    ON CONFLICT (key) DO UPDATE SET 
     a = EXCLUDED.a 
""") 

但這種失敗,因爲它顯然試圖插入fkeynull

psycopg2.IntegrityError: null value in column "fkey" violates not-null constraint 
DETAIL: Failing row contains (1, null, 0). 

語法是否真的正確?爲什麼失敗? SQLAlchemy對此錯誤有任何參與嗎?或者它是否正確地翻譯了PLSQL?

我懷疑是約束檢查發生的衝突解決之前觸發,所以雖然它實際上工作,因爲fkey是保證之前沒有空,也不會被覆蓋,約束檢查只着眼於暫定插入和表約束。

+0

PS:我知道我可以使用UPDATE而不是INSERT + ON CONFLICT,因爲行保證在那裏。我重複使用舊代碼,只是想知道它爲什麼失敗。 – VillasV

+0

你是對的,postgres評估UPSERT的限制,即使它總是會進入ON CONFLICT部分。你可以用很多方法解決這個問題,比如使用你提到的UPDATE,或者從約束切換到觸發AFTER事件,因爲它不會爲執行ON CONFLICT部分的行觸發。 –

回答

3

這是PostgreSQL的current documented limitation,它是一個破壞規範的區域。

當前,只有唯一,主鍵,引用(外鍵)和EXCLUDE約束受此設置的影響。 當插入或修改一行時(不在語句結尾處),NOT NULL和CHECK約束總是立即檢查。還沒有聲明爲DEFERRABLE的唯一性和排除約束也會立即檢查。

您不能推遲NOT NULL約束,並且您似乎明白默認行爲,請參見此處。

CREATE TABLE foo (a int NOT NULL, b int UNIQUE, c int); 
INSERT INTO foo (a,b,c) VALUES (1,2,3); 

INSERT INTO foo (b,c) VALUES (2,3); 
ERROR: null value in column "a" violates not-null constraint 
DETAIL: Failing row contains (null, 2, 3). 
相關問題