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?