Du kan implementera det utan smutsiga knep . förläng bara den främmande nyckeln hänvisar till det valda alternativet för att inkludera variable_id
förutom choice_id
.
Här är en fungerande demo. Tillfälliga bord, så att du enkelt kan spela med det:
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)
;
Att välja ett associerat alternativ är tillåtet:
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;
Men det går inte att komma ur linjen:
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".
Precis vad du ville ha.
Alla nyckelkolumner NOT NULL
Jag tror att jag hittade en bättre lösning i detta senare svar:
- Hur man hanterar ömsesidigt beroende skär
Att adressera @ypercubes fråga i kommentarerna, för att undvika poster med okänd association gör alla nyckelkolumner NOT NULL
, inklusive främmande nycklar.
Det cirkulära beroendet skulle normalt göra det omöjligt. Det är det klassiska kycklingägget problem:en av båda måste vara där först för att skapa den andra. Men naturen hittade en väg runt det, och det gjorde Postgres också:förskjutbara främmande nyckelbegränsningar .
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!
Ny variabler och associerade alternativ måste infogas i samma transaktion:
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
begränsning kan inte skjutas upp, den verkställs omedelbart. Men den främmande nyckeln kan , eftersom vi definierade det så. Det kontrolleras i slutet av transaktionen, vilket undviker höns-ägg-problemet.
I denna redigerade scenario, båda främmande nycklarna skjuts upp . Du kan ange variabler och alternativ i godtycklig ordning.
Du kan till och med få det att fungera med en vanlig icke-uppskjutbar FK-begränsning om du anger relaterade poster i båda tabellerna i ett uttalande använder CTE enligt beskrivningen i det länkade svaret.
Du kanske har märkt att den första främmande nyckelbegränsningen inte har någon CASCADE
modifierare. (Det vore inte vettigt att tillåta ändringar av variableoptions.variable_id
att kaskad tillbaka.
Å andra sidan har den andra främmande nyckeln en CASCADE
modifierare och definieras som DEFERRABLE
ändå. Detta har vissa begränsningar. Manualen:
Andra referensåtgärder än
NO ACTION
kontrollen kan inte skjutas upp, även om begränsningen förklaras uppskjuten.
NO ACTION
är standard.
Så, referensintegritetskontroller på INSERT
skjuts upp, men de deklarerade överlappande åtgärderna på DELETE
och UPDATE
är inte. Följande är inte tillåtet i PostgreSQL 9.0 eller senare eftersom begränsningar upprätthålls efter varje uttalande:
UPDATE option SET var_id = 4 WHERE var_id = 5;
DELETE FROM var WHERE var_id = 5;
Detaljer:
- Begränsning som definierats UPPHÖJBAR INitialt OMEDELBART är fortfarande UPPSKJUT?