PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Сложно ограничение на външния ключ в SQLAlchemy

Можете да го приложите без мръсни трикове . Просто разширете външния ключ препращане към избраната опция за включване на variable_id в допълнение към choice_id .

Ето работеща демонстрация. Временни маси, за да можете лесно да играете с тях:

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)
;

Изборът на свързана опция е разрешен:

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;

Но няма излизане от линията:

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

Точно това, което искахте.

Всички ключови колони NOT NULL

Мисля, че намерих по-добро решение в този по-късен отговор:

  • Как да се справяме с взаимно зависими вложки

Обръщайки се към въпроса на @ypercube в коментарите, за да избегнете записи с неизвестна асоциация, направете всички ключови колони NOT NULL , включително външни ключове.

Кръговата зависимост обикновено прави това невъзможно. Става дума за класическото пилешко яйце проблем:едното от двете трябва да е там първо, за да породи другия. Но природата намери начин да го заобиколи, както и Postgres:ограничения за външни ключове с възможност за отлагане .

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!

Ново променливите и свързаните опции трябва да бъдат вмъкнати в една и съща транзакция:

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 ограничението не може да бъде отложено, то се прилага незабавно. Но ограничението на външния ключ може , защото така го дефинирахме. Проверява се в края на транзакцията, което избягва проблема пиле-яйце.

В товаредакт.та сценарий, и двата външни ключа са отложени . Можете да въвеждате променливи и опции в произволна последователност.
Можете дори да го накарате да работи с обикновено неотлагаемо FK ограничение, ако въведете свързани записи в двете таблици в един израз използвайки CTE, както е подробно описано в свързания отговор.

Може да сте забелязали, че първото ограничение за външен ключ няма CASCADE модификатор. (Не би имало смисъл да се разрешават промени в variableoptions.variable_id за каскадно връщане назад.

От друга страна, вторият външен ключ има CASCADE модификатор и е дефиниран DEFERRABLE въпреки това. Това носи някои ограничения. Ръководството:

Референтни действия, различни от NO ACTION проверката не може да бъде отложена, дори ако ограничението е декларирано за отлагане.

NO ACTION е по подразбиране.

И така, проверките на референтната цялост на INSERT се отлагат, но декларираните каскадни действия при DELETE и UPDATE не са. Следното не е разрешено в PostgreSQL 9.0 или по-нова версия, тъй като ограниченията се прилагат след всеки израз:

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

Подробности:

  • Дефинираното ограничение ОТЛОЖЕНО ПЪРВОНАЧАЛНО НЕЗАБАВНО все още е ОТЛОЖЕНО?


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Ruby 'pg' gem, свързващ грешно копие на libpq.5.dylib (на OSX)

  2. Актуализирайте няколко реда в една и съща заявка с помощта на PostgreSQL

  3. INSERT IN... FROM SELECT ... ВРЪЩАНЕ на идентификационните съпоставяния

  4. PostgreSQL не използва частичен индекс

  5. Разбиране на ограниченията за проверка в PostgreSQL