PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Złożone ograniczenie klucza obcego w SQLAlchemy

Możesz to wdrożyć bez brudnych sztuczek . Po prostu rozszerz klucz obcy odwołując się do wybranej opcji, aby uwzględnić variable_id oprócz choice_id .

Oto działające demo. Stoły tymczasowe, dzięki którym możesz łatwo się nimi bawić:

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

Dozwolony jest wybór powiązanej opcji:

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 nie ma wyjścia:

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

Dokładnie to, czego chciałeś.

Wszystkie kluczowe kolumny NOT NULL

Myślę, że znalazłem lepsze rozwiązanie w tej późniejszej odpowiedzi:

  • Jak radzić sobie z wzajemnie zależnymi wstawkami

Odnosząc się do pytania @ypercube w komentarzach, aby uniknąć wpisów z nieznanym powiązaniem, ustaw wszystkie kluczowe kolumny NOT NULL , w tym klucze obce.

Zależność kołowa normalnie by to uniemożliwiła. To klasyczne jajko kurze problem:jeden z nich musi być pierwszy, aby odrodzić się drugi. Ale natura znalazła sposób na obejście tego, podobnie jak Postgres:odroczone ograniczenia klucza obcego .

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!

Nowy zmienne i powiązane opcje muszą być wstawione w tej samej transakcji:

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 ograniczenie nie może być odroczone, jest wymuszane natychmiast. Ale ograniczenie klucza obcego może , ponieważ tak to zdefiniowaliśmy. Jest to sprawdzane pod koniec transakcji, co pozwala uniknąć problemu z jajkiem kurzym.

W tym edytowanym scenariusz, oba klucze obce są odroczone . Możesz wprowadzać zmienne i opcje w dowolnej kolejności.
Możesz nawet sprawić, by działały ze zwykłym nieodroczonym ograniczeniem FK, jeśli wprowadzisz powiązane wpisy w obu tabelach w jednej instrukcji przy użyciu CTE zgodnie z opisem w odpowiedzi, do której prowadzi link.

Być może zauważyłeś, że pierwsze ograniczenie klucza obcego nie ma CASCADE modyfikator. (Nie ma sensu zezwalać na zmiany w variableoptions.variable_id by wrócić kaskadą.

Z drugiej strony drugi klucz obcy ma CASCADE modyfikator i jest zdefiniowany jako DEFERRABLE niemniej jednak. To niesie ze sobą pewne ograniczenia. Instrukcja:

Działania referencyjne inne niż NO ACTION sprawdzenie nie może być odroczone, nawet jeśli ograniczenie jest zadeklarowane jako możliwe do odroczenia.

NO ACTION jest ustawieniem domyślnym.

Tak więc sprawdzanie integralności referencyjnej na INSERT są odroczone, ale zadeklarowane działania kaskadowe na DELETE i UPDATE nie są. Następujące czynności nie są dozwolone w PostgreSQL 9.0 lub nowszych, ponieważ po każdej instrukcji wymuszane są ograniczenia:

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

Szczegóły:

  • Zdefiniowane ograniczenie ODROCZONE POCZĄTKOWO NATYCHMIAST jest nadal ODROCZONE?


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Połączenie Django z postgresem przez docker-compose

  2. Ograniczenie sprawdzania PostgreSQL dla warunku klucza obcego

  3. Prosta konfiguracja replikacji Slony-I.

  4. Jak działa justify_days() w PostgreSQL

  5. Dodaj interwał do znacznika czasu za pomocą fragmentów Ecto