簡體   English   中英

SQLAlchemy中復雜的外鍵約束

[英]Complex foreign key constraint in SQLAlchemy

我有兩個表, SystemVariablesVariableOptions SystemVariables應該是不言自明的, VariableOptions包含所有VariableOptions所有可能選擇。

VariableOptions有一個外鍵variable_id ,它指出了哪個變量是一個選項。 SystemVariables有一個外鍵, choice_id ,它指出哪個選項是當前選擇的選項。

我已經得到了,周圍用循環關系use_alterchoice_id ,並post_updateSystemVariableschoice關系。 但是,我想添加一個額外的數據庫約束,以確保choice_id有效(即它指的是引用它的選項)。

我需要的邏輯,假設sysVar代表SystemVariables表中的SystemVariables ,基本上是:

VariableOptions[sysVar.choice_id].variable_id == sysVar.id

但我不知道如何使用SQL,聲明式或任何其他方法構造這種約束。 如果有必要,我可以在應用程序級別驗證這一點,但如果可能的話,我想在數據庫級別進行驗證。 我正在使用Postgres 9.1。

這可能嗎?

你可以在沒有骯臟技巧的情況下實現。 只需擴展引用所選選項的外鍵 ,除了choice_id之外還要包含variable_id

這是一個工作演示。 臨時表,所以你可以輕松玩它:

CREATE TEMP TABLE systemvariables (
  variable_id integer PRIMARY KEY
, variable    text
, choice_id   integer
);

INSERT INTO systemvariables(variable_id, variable)
VALUES
  (1, 'var1')
, (2, 'var2')
, (3, 'var3');

CREATE TEMP TABLE variableoptions (
  option_id integer PRIMARY KEY
, option text
, variable_id integer REFERENCES systemvariables(variable_id)
                      ON UPDATE CASCADE ON DELETE CASCADE
, UNIQUE (option_id, variable_id) -- needed for the foreign key
);

ALTER TABLE systemvariables
ADD CONSTRAINT systemvariables_choice_id_fk
   FOREIGN KEY (choice_id, variable_id)
   REFERENCES variableoptions(option_id, variable_id);

INSERT INTO variableoptions
VALUES
  (1, 'var1_op1', 1)
, (2, 'var1_op2', 1)
, (3, 'var1_op3', 1)
, (4, 'var2_op1', 2)
, (5, 'var2_op2', 2)
, (6, 'var3_op1', 3);

允許選擇關聯選項:

UPDATE systemvariables SET choice_id = 2 WHERE variable_id = 1;
UPDATE systemvariables SET choice_id = 5 WHERE variable_id = 2;
UPDATE systemvariables SET choice_id = 6 WHERE variable_id = 3;

但是沒有脫節:

UPDATE systemvariables SET choice_id = 7 WHERE variable_id = 3;
UPDATE systemvariables SET choice_id = 4 WHERE variable_id = 1;
 ERROR: insert or update on table "systemvariables" violates foreign key constraint "systemvariables_choice_id_fk" DETAIL: Key (choice_id,variable_id)=(4,1) is not present in table "variableoptions". 

Voilá 正是你想要的。


所有鍵列NOT NULL

我想我在后面的回答中找到了一個更好的解決方案:

在注釋中解決@ ypercube的問題 ,以避免具有未知關聯的條目使所有鍵列NOT NULL ,包括外鍵。

循環依賴通常會使這種情況變得不可能。 這是經典的雞蛋問題:兩者中的一個必須先在那里產生另一個。 但大自然找到了解決方法,Postgres也是如此: 可延遲的外鍵約束

CREATE TEMP TABLE systemvariables (
  variable_id integer PRIMARY KEY
, variable    text
, choice_id   integer NOT NULL
);

CREATE TEMP TABLE variableoptions (
  option_id   integer PRIMARY KEY
, option      text
, variable_id integer NOT NULL
     REFERENCES systemvariables(variable_id)
     ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED
, UNIQUE (option_id, variable_id) -- needed for the foreign key
);

ALTER TABLE systemvariables
ADD CONSTRAINT systemvariables_choice_id_fk FOREIGN KEY (choice_id, variable_id)
   REFERENCES variableoptions(option_id, variable_id)
   DEFERRABLE INITIALLY DEFERRED; -- no CASCADING here!

必須在同一事務中插入變量和相關選項:

BEGIN;

INSERT INTO systemvariables (variable_id, variable, choice_id)
VALUES
  (1, 'var1', 2)
, (2, 'var2', 5)
, (3, 'var3', 6);

INSERT INTO variableoptions (option_id, option, variable_id)
VALUES
  (1, 'var1_op1', 1)
, (2, 'var1_op2', 1)
, (3, 'var1_op3', 1)
, (4, 'var2_op1', 2)
, (5, 'var2_op2', 2)
, (6, 'var3_op1', 3);

END;

NOT NULL約束不能延遲,它會立即強制執行。 但是外鍵約束可以 ,因為我們這樣定義它。 在交易結束時檢查,這避免了雞蛋問題。

在此編輯的方案中, 兩個外鍵都是延遲的 您可以按任意順序輸入變量和選項。

您可能已經注意到第一個外鍵約束沒有CASCADE修飾符。 (允許對variableoptions.variable_id更改進行級聯反應是沒有意義的。

另一方面,第二個外鍵具有CASCADE修飾符,並且仍被定義為可延遲。 這帶來了一些限制。 手冊

即使約束被聲明為可延遲,也不能延遲除NO ACTION檢查以外的參考動作。

NO ACTION是默認值。

因此,對INSERT參照完整性檢查是延遲的,但DELETEUPDATE上聲明的級聯操作不是。 PostgreSQL 9.0或9.1中不允許以下內容,因為在每個語句后強制執行約束:

UPDATE option SET var_id = 4 WHERE var_id = 5;
DELETE FROM var WHERE var_id = 5;

細節:

奇怪的是,同樣的事情在PostgreSQL 8.4中有效 ,而文檔聲稱具有相同的行為。 看起來像舊版本中的一個錯誤 - 即使它看起來有益而不是乍一看有害。 必須已針對較新版本進行修復。

編輯: SQLAlchemy的0.7.4版本(在我開始詢問此問題的同一天發布,7/12 / '11!),包含主鍵的新autoincrement值,也是外鍵的一部分, ignore_fk 文檔也已經擴展到包括我最初想要完成的一個很好的例子。

現在所有人都在這里解釋清楚。

如果您想查看我在上述版本之前提出的代碼,請查看此答案的修訂歷史記錄。

我真的不喜歡循環引用。 通常有一種方法可以避免它們。 這是一種方法:

SystemVariables 
---------------
  variable_id 
  PRIMARY KEY (variable_id)


VariableOptions 
---------------
  option_id 
  variable_id 
  PRIMARY KEY (option_id)
  UNIQUE KEY (variable_id, option_id) 
  FOREIGN KEY (variable_id) 
    REFERENCES SystemVariables(variable_id)


CurrentOptions
--------------
  variable_id 
  option_id 
  PRIMARY KEY (variable_id)
  FOREIGN KEY (variable_id, option_id)
    REFERENCES VariableOptions(variable_id, option_id)

暫無
暫無

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

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