Je kunt dat zonder vuile trucs . implementeren . Gewoon verleng de externe sleutel verwijzend naar de gekozen optie om variable_id
. op te nemen naast choice_id
.
Hier is een werkende demo. Tijdelijke tafels, zodat je er gemakkelijk mee kunt spelen:
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)
;
Het kiezen van een bijbehorende optie is toegestaan:
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;
Maar er is geen ontkomen aan:
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".
Precies wat je wilde.
Alle sleutelkolommen NOT NULL
Ik denk dat ik in dit latere antwoord een betere oplossing heb gevonden:
- Hoe om te gaan met wederzijds afhankelijke tussenvoegsels
Behandel de vraag van @ypercube in de opmerkingen, om invoer met onbekende associatie te voorkomen, maak alle sleutelkolommen NOT NULL
, inclusief externe sleutels.
De circulaire afhankelijkheid zou dat normaal gesproken onmogelijk maken. Het is het klassieke kip-ei probleem:een van beide moet er als eerste zijn om de ander te spawnen. Maar de natuur heeft er een manier omheen gevonden, en Postgres ook:uitstelbare externe sleutelbeperkingen .
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!
Nieuw variabelen en bijbehorende opties moeten in dezelfde transactie worden ingevoegd:
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;
De NOT NULL
beperking kan niet worden uitgesteld, het wordt onmiddellijk afgedwongen. Maar de externe sleutelbeperking kan , omdat we het zo hebben gedefinieerd. Het wordt gecontroleerd aan het einde van de transactie, waardoor het kip-ei-probleem wordt vermeden.
In deze bewerkte scenario, beide externe sleutels worden uitgesteld . U kunt variabelen en opties in willekeurige volgorde invoeren.
U kunt het zelfs laten werken met een duidelijke niet-uitstelbare FK-beperking als u gerelateerde items in beide tabellen invoert in één instructie met behulp van CTE's zoals beschreven in het gekoppelde antwoord.
Het is je misschien opgevallen dat de eerste beperking van de refererende sleutel geen CASCADE
. heeft modificator. (Het zou geen zin hebben om wijzigingen toe te staan aan variableoptions.variable_id
om terug te stromen.
Aan de andere kant heeft de tweede externe sleutel een CASCADE
modifier en is gedefinieerd DEFERRABLE
niettemin. Dit brengt enkele beperkingen met zich mee. De handleiding:
Andere referentiële acties dan de
NO ACTION
controle kan niet worden uitgesteld, zelfs niet als de beperking uitstelbaar wordt verklaard.
NO ACTION
is de standaardinstelling.
Dus, referentiële integriteitscontroles op INSERT
worden uitgesteld, maar de gedeclareerde trapsgewijze acties op DELETE
en UPDATE
zijn niet. Het volgende is niet toegestaan in PostgreSQL 9.0 of hoger omdat er na elke instructie beperkingen worden opgelegd:
UPDATE option SET var_id = 4 WHERE var_id = 5;
DELETE FROM var WHERE var_id = 5;
Details:
- Beperking gedefinieerd UITSTELBAAR AANLEIDING ONMIDDELLIJK is nog steeds UITGESTELD?