sql >> Database teknologi >  >> RDS >> PostgreSQL

Kompleks fremmednøglebegrænsning i SQLAlchemy

Du kan implementere det uden beskidte tricks . Bare forlæng fremmednøglen henviser til den valgte mulighed for at inkludere variable_id ud over choice_id .

Her er en fungerende demo. Midlertidige borde, så du nemt kan spille 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)
;

Det er tilladt at vælge en tilknyttet indstilling:

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 der er ingen mulighed for at komme ud af køen:

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

Præcis hvad du ønskede.

Alle nøglekolonner NOT NULL

Jeg tror, ​​jeg fandt en bedre løsning i dette senere svar:

  • Sådan håndterer du gensidigt afhængige skær

Adressering af @ypercubes spørgsmål i kommentarerne, for at undgå poster med ukendt tilknytning gør alle nøglekolonner til NOT NULL , inklusive fremmednøgler.

Den cirkulære afhængighed ville normalt gøre det umuligt. Det er det klassiske kylling-æg problem:den ene af begge skal være der først for at skabe den anden. Men naturen fandt en vej uden om det, og det gjorde Postgres også:udskydelige fremmednøglebegrænsninger .

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 og tilknyttede muligheder skal indsættes i samme 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 ikke udskydes, den håndhæves straks. Men den fremmednøgle-begrænsning kan , fordi vi definerede det sådan. Det kontrolleres i slutningen af ​​transaktionen, hvilket undgår hønseæg-problemet.

I denne redigerede scenario, begge fremmednøgler er udskudt . Du kan indtaste variabler og muligheder i vilkårlig rækkefølge.
Du kan endda få det til at fungere med almindelig ikke-udskydelig FK-begrænsning, hvis du indtaster relaterede poster i begge tabeller i én sætning ved at bruge CTE'er som beskrevet i det linkede svar.

Du har måske bemærket, at den første fremmednøgle-begrænsning ikke har nogen CASCADE modifikator. (Det ville ikke give mening at tillade ændringer af variableoptions.variable_id at kaskade tilbage.

På den anden side har den anden fremmednøgle en CASCADE modifikator og er defineret DEFERRABLE Ikke desto mindre. Dette har nogle begrænsninger. Manualen:

Andre referencehandlinger end NO ACTION check kan ikke udskydes, selvom begrænsningen er erklæret udskydelig.

NO ACTION er standard.

Så referentiel integritet kontrollerer INSERT er udskudt, men de erklærede kaskadehandlinger på DELETE og UPDATE er ikke. Følgende er ikke tilladt i PostgreSQL 9.0 eller nyere, fordi begrænsninger håndhæves efter hver erklæring:

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

Detaljer:

  • Begrænsning, der er defineret, UDSKESKELIG I UDEN INDLEDNING, er stadig UDSAT?


  1. SYSDATETIMEOFFSET() Eksempler i SQL Server (T-SQL)

  2. JPA Tabelnavne med store bogstaver

  3. Søg om tal er indeholdt i et udtryk som:1-3,5,10-15,20

  4. Tips til brug af SQL Server med Salesforce SOQL