sql >> Database >  >> RDS >> PostgreSQL

Complexe externe-sleutelbeperking in SQLAlchemy

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?


  1. Hoe OCT() werkt in MariaDB

  2. Begrijp groep per clausule in SQL Server - SQL Server / TSQL-zelfstudie, deel 130

  3. Galera Cluster Cloud-aanbiedingen vergelijken:deel twee Google Cloud Platform (GCP)

  4. Hoe genereer je een reeks getallen tussen twee getallen?