PostgreSQL
 sql >> Teknologi Basis Data >  >> RDS >> PostgreSQL

Batasan kunci asing yang kompleks di SQLAlchemy

Anda dapat menerapkannya tanpa trik kotor . Cukup perpanjang kunci asing mereferensikan opsi yang dipilih untuk menyertakan variable_id selain choice_id .

Ini adalah demo yang berfungsi. Tabel sementara, sehingga Anda dapat dengan mudah memainkannya:

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

Memilih opsi terkait diperbolehkan:

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;

Tapi tidak ada yang keluar dari barisan:

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

Persis seperti yang Anda inginkan.

Semua kolom kunci NOT NULL

Saya pikir saya menemukan solusi yang lebih baik dalam jawaban ini nanti:

  • Cara menangani sisipan yang saling bergantung

Mengatasi pertanyaan @ypercube di komentar, untuk menghindari entri dengan asosiasi yang tidak dikenal, buat semua kolom kunci NOT NULL , termasuk kunci asing.

Ketergantungan melingkar biasanya membuat itu tidak mungkin. Ini adalah telur ayam klasik masalah:salah satu dari keduanya harus ada terlebih dahulu untuk menelurkan yang lain. Tetapi alam menemukan jalan keluarnya, dan begitu pula Postgres:kendala kunci asing yang dapat ditangguhkan .

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!

Baru variabel dan opsi terkait harus dimasukkan dalam transaksi yang sama:

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 kendala tidak dapat ditangguhkan, itu ditegakkan segera. Tetapi batasan kunci asing bisa , karena kami mendefinisikannya seperti itu. Itu diperiksa di akhir transaksi, yang menghindari masalah ayam-telur.

Dalam diedit . ini skenario, kedua kunci asing ditangguhkan . Anda dapat memasukkan variabel dan opsi dalam urutan arbitrer.
Anda bahkan dapat membuatnya bekerja dengan batasan FK yang tidak dapat ditangguhkan jika Anda memasukkan entri terkait di kedua tabel dalam satu pernyataan menggunakan CTE seperti yang dijelaskan dalam jawaban tertaut.

Anda mungkin telah memperhatikan bahwa batasan kunci asing pertama tidak memiliki CASCADE pengubah. (Tidak masuk akal untuk mengizinkan perubahan pada variableoptions.variable_id untuk mengalir kembali.

Di sisi lain, kunci asing kedua memiliki CASCADE pengubah dan didefinisikan DEFERRABLE Namun. Ini membawa beberapa batasan. Panduan:

Tindakan referensi selain NO ACTION cek tidak dapat ditangguhkan, bahkan jika batasan dinyatakan dapat ditangguhkan.

NO ACTION adalah default.

Jadi, pemeriksaan integritas referensial pada INSERT ditangguhkan, tetapi tindakan cascading yang dideklarasikan pada DELETE dan UPDATE tidak. Berikut ini tidak diizinkan di PostgreSQL 9.0 atau yang lebih baru karena batasan diberlakukan setelah setiap pernyataan:

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

Detail:

  • Kendala yang ditentukan TUNDA AWAL SEGERA masih DITUNDA?


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. datagrip Tidak dapat menerapkan perubahan Tabel ini hanya dapat dibaca. Perubahan editor sel tidak dapat diterapkan

  2. Bisakah tipe data Postgres NUMERIC menyimpan nilai yang ditandatangani?

  3. Lingkup rel - di mana dalam kecocokan yang tepat

  4. SQL INSERT tanpa menentukan kolom. Apa yang terjadi?

  5. XMLTABLE di PostgreSQL