sql >> Databáze >  >> RDS >> PostgreSQL

Složité omezení cizího klíče v SQLAlchemy

Můžete to implementovat bez špinavých triků . Stačí prodloužit cizí klíč odkazem na vybranou možnost tak, aby zahrnovala variable_id kromě choice_id .

Zde je funkční demo. Dočasné stoly, takže s nimi můžete snadno hrát:

CREATE TABLE systemvariables (
  variable_id int PRIMARY KEY
, choice_id   int
, variable    text
);
   
INSERT INTO systemvariables(variable_id, variable) VALUES
  (1, 'var1')
, (2, 'var2')
, (3, 'var3')
;

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

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

Výběr přidružené možnosti je povolen:

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;

Ale není možné vybočit z řady:

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".

Přesně to, co jste chtěli.

Všechny klíčové sloupce NOT NULL

Myslím, že jsem v této pozdější odpovědi našel lepší řešení:

  • Jak zacházet se vzájemně závislými vložkami

Řešení otázky @ypercube v komentářích, abyste se vyhnuli záznamům s neznámou asociací, nastavte všechny klíčové sloupce NOT NULL včetně cizích klíčů.

Kruhová závislost by to normálně znemožnila. Je to klasické kuřecí vejce problém:jeden z obou tam musí být první, aby se mohl objevit ten druhý. Ale příroda si našla cestu, jak to obejít, a stejně tak Postgres:odložitelná omezení cizích klíčů .

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

CREATE TABLE variableoptions (
  option_id   int PRIMARY KEY
, option      text
, variable_id int NOT NULL REFERENCES systemvariables
     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!

Nové proměnné a související možnosti musí být vloženy do stejné transakce:

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 omezení nelze odložit, je uplatněno okamžitě. Ale omezení cizího klíče může , protože jsme to tak definovali. Kontroluje se na konci transakce, čímž se vyhnete problému kuřecích vajec.

V tomto upraveno scénář oba cizí klíče jsou odloženy . Proměnné a možnosti můžete zadávat v libovolném pořadí.
Můžete to dokonce zajistit, aby to fungovalo s prostým neodložitelným omezením FK, pokud zadáte související položky do obou tabulek v jednom příkazu pomocí CTE, jak je podrobně popsáno v odkazované odpovědi.

Možná jste si všimli, že první omezení cizího klíče nemá CASCADE modifikátor. (Nedávalo by smysl povolit změny variableoptions.variable_id kaskádovat zpět.

Na druhou stranu druhý cizí klíč má CASCADE modifikátor a je definován DEFERRABLE nicméně. To s sebou nese určitá omezení. Manuál:

Referenční akce jiné než NO ACTION kontrolu nelze odložit, i když je omezení prohlášeno za odložitelné.

NO ACTION je výchozí.

Takže referenční integrita kontroluje INSERT jsou odloženy, ale deklarované kaskádové akce na DELETE a UPDATE nejsou. V PostgreSQL 9.0 nebo novějším není povoleno následující, protože po každém příkazu jsou vynucena omezení:

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

Podrobnosti:

  • Je stále ODLOŽENO ODLOŽIT PŮVODNĚ OKAMŽITĚ?


  1. Proč (a jak) rozdělit sloupec pomocí master..spt_values?

  2. problém s použitím parametrů Oracle v SELECT IN

  3. Postgres výpis pouze částí tabulek pro vývojový snímek

  4. Vyhledávání databázových objektů a dat tabulek na serveru SQL