Indeks tidak dapat ditunda - tidak masalah apakah itu UNIQUE
sebagian atau tidak, hanya UNIQUE
kendala. Jenis lain dari kendala ( FOREIGN KEY
, PRIMARY KEY
, EXCLUDE
) juga deferrable - tetapi tidak CHECK
kendala.
Jadi indeks parsial unik (dan kendala implisit yang diterapkannya) akan diperiksa pada setiap pernyataan (dan faktanya setelah setiap baris memasukkan / memperbarui dalam implementasi saat ini), bukan pada akhir transaksi.
Apa yang bisa Anda lakukan, jika Anda ingin mengimplementasikan batasan ini sebagai ditangguhkan, adalah menambahkan satu tabel lagi dalam desain. Sesuatu seperti ini:
CREATE TABLE public.booking_status
( booking_id int NOT NULL, -- same types
check_in timestamp NOT NULL, -- as in
check_out timestamp NOT NULL, -- booking
CONSTRAINT unique_booking
UNIQUE (check_in, check_out)
DEFERRABLE INITIALLY DEFERRED,
CONSTRAINT unique_booking_fk
FOREIGN KEY (booking_id, check_in, check_out)
REFERENCES public.booking (booking_id, check_in, check_out)
DEFERRABLE INITIALLY DEFERRED
) ;
Dengan desain ini dan dengan asumsi bahwa booking_status
hanya memiliki 2 opsi yang memungkinkan (0 dan 1), Anda dapat menghapus seluruhnya dari booking
(jika ada baris pada booking_status
, itu adalah 1, jika tidak adalah 0).
Cara lain adalah dengan (ab) menggunakan EXCLUDE
batasan:
ALTER TABLE booking
ADD CONSTRAINT unique_booking
EXCLUDE
( check_in WITH =,
check_out WITH =,
(CASE WHEN booking_status = 1 THEN TRUE END) WITH =
)
DEFERRABLE INITIALLY DEFERRED ;
Diuji di dbfiddle .
Apa yang dilakukan di atas:
The CASE
ekspresi menjadi NULL
saat booking_status
null atau berbeda dari 1. Kita bisa menulis (CASE WHEN booking_status = 1 THEN TRUE END)
seperti (booking_status = 1 OR NULL)
apakah yang membuat itu lagi jelas.
Batasan unik dan kecualikan menerima baris di mana satu atau lebih ekspresi NULL. Jadi berfungsi sebagai indeks yang difilter dengan WHERE booking_status = 1
.
Semua WITH
operator =
itu bertindak sebagai UNIQUE
kendala.
Gabungan keduanya ini membuat batasan bertindak sebagai indeks unik yang difilter.
Tapi itu kendala dan EXCLUDE
kendala bisa ditunda.
(CASE WHEN booking_status = 1 THEN TRUE END) WITH =)
harus diganti dengan) WHERE (booking_status = 1)
karena "Pengecualian kendala diimplementasikan menggunakan indeks", dan indeks parsial iniWHERE
akan lebih kecil dan lebih cepat - postgresql.org/docs/current/sql-createtable.html dan postgresql.org/docs/current/sql- createindex.htmlMeskipun tahun-tahun pertanyaan ini telah berlalu, saya ingin mengklarifikasi untuk penutur bahasa Spanyol, tes telah dilakukan di Postgres:
Kendala berikut telah ditambahkan ke tabel catatan 1337, di mana kit adalah kunci utama:
Ini menciptakan kunci utama default TIDAK DITAWARKAN untuk tabel sehingga ketika mencoba PEMBARUAN berikutnya kita mendapatkan kesalahan:
Di Postgres, menjalankan UPDATE untuk setiap ROW memverifikasi bahwa PEMBATASAN atau KONSTRAINT terpenuhi.
SEGERA CONSTRAINT sekarang dibuat dan setiap pernyataan dieksekusi secara terpisah:
Di sini SI memungkinkan mengubah kunci utama karena mengeksekusi seluruh kalimat lengkap pertama (1328 baris); tetapi meskipun sedang dalam transaksi (BEGIN), CONSTRAINT segera divalidasi setelah menyelesaikan setiap kalimat tanpa membuat KOMIT, oleh karena itu menghasilkan kesalahan ketika menjalankan INSERT. Akhirnya kami membuat CONSTRAINT DEFERRED melakukan hal berikut:
Jika kami menjalankan setiap pernyataan ** Blok 2 **, setiap kalimat secara terpisah, tidak ada kesalahan yang dihasilkan ke INSERT karena itu tidak valid tetapi KOMIT akhir dieksekusi di mana ia menemukan ketidakkonsistenan.
Untuk informasi lengkap dalam bahasa Inggris, saya sarankan Anda memeriksa tautannya:
Kendala SQL yang Ditangguhkan dalam Kedalaman
BUKAN DITANGGUNG JAWAB SEGERA DITERBITKAN
sumber