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?