TIDAK DAPAT DIUBAH versus DAPAT DITERBITKAN SEGERA

92

Saya membaca ini tentang kata kunci SQL DEFERRABLEdalam Sistem Database - Buku Lengkap .

Yang terakhir [TIDAK DAPAT DITERBITKAN] adalah default, dan berarti bahwa setiap kali pernyataan modifikasi database dijalankan, batasan diperiksa segera setelahnya, jika modifikasi dapat melanggar batasan kunci asing.

Namun, jika kita mendeklarasikan sebuah batasan sebagai DEFERRABLE , maka kita memiliki pilihan untuk menunggu sampai transaksi selesai sebelum memeriksa batasan tersebut.

Kami mengikuti kata kunci deferrable oleh salah AWALNYA DITANGGUHKAN atau AWALNYA SEGERA . Dalam kasus sebelumnya, pemeriksaan akan ditangguhkan sebelum setiap transaksi dilakukan. Dalam kasus terakhir, pemeriksaan akan dilakukan segera setelah setiap pernyataan.

Apa NOT DEFERRABLEbedanya dengan DEFERRABLE INITIALLY IMMEDIATE? Dalam kedua kasus, tampaknya, setiap kendala diperiksa setelah setiap pernyataan individu.

Pieter
sumber

Jawaban:

74

Dengan DEFERRABLE INITIALLY IMMEDIATE Anda dapat menunda kendala sesuai permintaan saat Anda membutuhkannya.

Ini berguna jika Anda biasanya ingin memeriksa batasan pada waktu pernyataan, tetapi misalnya beban batch ingin menunda pemeriksaan sampai waktu komit.

Sintaks cara menunda batasan berbeda untuk berbagai DBMS.

Dengan NOT DEFERRABLEAnda tidak akan pernah bisa menunda pemeriksaan sampai waktu komit.

seekor kuda tanpa nama
sumber
1
@romkyns: DEFERRABLEmenyatakan niat desainer bahwa menunda batasan adalah tindakan yang berharga atau perlu. Ini tidak terjadi untuk sebagian besar kendala database dan pelabelan semua karena DEFERRABLEakan kehilangan perbedaan yang berguna ini.
onedaywhen
4
@onedaywhen Seorang kolega saya sejak itu menunjukkan alasan valid yang bagus, sebenarnya: Anda dapat mengandalkan batasan yang tidak dapat ditangguhkan di titik mana pun dalam transaksi apa pun, tetapi batasan yang dapat ditangguhkan hanya pasti diamati pada awal transaksi.
Roman Starkov
@RomanStarkov Juga, ini masalah kinerja. NOT DEFERRABLEbiasanya yang tercepat.
Teejay
49

Selain jawaban (benar) lainnya, ketika berbicara tentang PostgreSQL harus dinyatakan bahwa:

  • dengan NOT DEFERRABLE setiap baris diperiksa pada waktu penyisipan / pembaruan

  • dengan DEFERRABLE (saat ini SEGERA ) semua baris diperiksa di akhir penyisipan / pembaruan

  • dengan deferrable (saat DITANGGUHKAN ) semua baris diperiksa pada akhir transaksi

Jadi tidak benar untuk mengatakan bahwa batasan DEFERRABLE bertindak seperti TIDAK DAPAT DITERBITKAN jika disetel ke SEGERA.


Mari kita uraikan perbedaan ini:

CREATE TABLE example(
    row integer NOT NULL,
    col integer NOT NULL,
    UNIQUE (row, col) DEFERRABLE INITIALLY IMMEDIATE
);

INSERT INTO example (row, col) VALUES (1,1),(2,2),(3,3);

UPDATE example SET row = row + 1, col = col + 1;

SELECT * FROM example;

Ini dengan benar menghasilkan:

keluaran

Tapi jika kita menghapus instruksi AWAL SEGERA DITERBITKAN,

EROR: nilai kunci duplikat melanggar batasan unik "example_row_col_key" DETAIL: Key ("row", col) = (2, 2) sudah ada. ********** Kesalahan **********

EROR: nilai kunci duplikat melanggar batasan unik "example_row_col_key" Status SQL: 23505 Detail: Key ("row", col) = (2, 2) sudah ada.


ADDENDUM (12 Oktober 2017)

Perilaku ini memang didokumentasikan di sini , bagian "Kompatibilitas":

Selain itu, PostgreSQL segera memeriksa batasan keunikan yang tidak dapat ditangguhkan, bukan di akhir pernyataan seperti yang disarankan standar.

Teejay
sumber
Menarik. Bagi saya, pada dasarnya tidak ada alasan untuk lebih memilih perilaku "TIDAK DAPAT DITERBITKAN" daripada perilaku "SEGERA" dan akan masuk akal bagi Postgres untuk lebih memaafkan dan membuat perilaku TIDAK DAPAT TERBEDA seperti "SEGERA". Urutan di mana baris diperbarui dalam pernyataan UPDATE bahkan tidak didokumentasikan atau ditentukan sejauh yang saya tahu, jadi bukankah perilaku pemeriksaan batasan pernyataan tengah ini setidaknya sebagian tidak ditentukan? Saya tidak mengerti mengapa ada orang yang menginginkan perilaku itu - tetapi mungkin ada kasus penggunaan yang masuk akal untuk itu sehingga saya kurang imajinasi untuk melihatnya.
Mark Amery
1
Ya, pada dasarnya satu-satunya alasan untuk memilih NOT DEFERRABLEadalah kecepatan ( lihat di sini , bagian Batasan Keunikan yang Tidak Ditangguhkan , "Sadarilah bahwa ini bisa jauh lebih lambat daripada pemeriksaan keunikan langsung" ).
Teejay
Selain itu, DEFERRABLEbatasan tidak dapat dirujuk sebagai kunci asing di tabel lain ( lihat di sini , bagian Parameter , "Kolom yang direferensikan harus merupakan kolom dari batasan kunci utama atau unik yang tidak dapat ditangguhkan dalam tabel yang direferensikan" ).
Teejay
1
Jadi sebagai aturan praktis, jika tidak terlalu penting bagi logika bisnis ketika batasan dicentang, maka haruskah saya menggunakan default NOT DEFERRABLEhanya karena kinerjanya lebih baik?
dvtan
1
Dalam ORM kami sekarang menggunakan aturan berikut: 1) jika batasannya adalah PK, kami menggunakan NOT DEFERRABLE2) jika setidaknya satu FK mereferensikan batasan, kami menggunakan NOT DEFERRABLEjuga 3) dalam kasus lain, kami menggunakan DEFERRABLE INITIALLY IMMEDIATE. Ini dapat sedikit menurunkan kinerja kendala tersebut, tetapi memastikan kompatibilitas maksimum dengan DBMS lain yang kami gunakan (Oracle, SqlServer). PK dan FK tidak menjadi masalah karena kami tidak pernah memperbarui nilai mereka (yang menurut saya merupakan kebiasaan pemrograman yang baik untuk DB).
Teejay
29

Selain jelas bisa menunda, perbedaan sebenarnya adalah kinerja. Jika tidak ada penalti kinerja maka tidak perlu memiliki opsi untuk memilih dapat ditangguhkan atau tidak - semua kendala hanya akan dapat ditangguhkan.

Hukuman kinerja berkaitan dengan pengoptimalan yang dapat dilakukan database dengan pengetahuan tentang bagaimana data dibatasi. Misalnya, indeks yang dibuat untuk mendukung batasan unik di Oracle tidak dapat menjadi indeks unik jika batasan tersebut dapat ditangguhkan karena mengizinkan duplikat untuk sementara waktu. Namun, jika batasan tidak dapat ditangguhkan maka indeks bisa menjadi unik.

Ryan
sumber
7

Saya sangat terlambat ke pesta tetapi saya ingin menambahkannya - per Desember 2018 - hanya dua database yang saya ketahui (mungkin ada lebih banyak) yang menawarkan beberapa level implementasi fitur SQL standar ini :

Database    NOT DEFERRABLE  DEFERRABLE           DEFERRABLE 
                            INITIALLY IMMEDIATE  INITIALLY DEFERRED
----------  --------------  -------------------  ------------------
Oracle      N/A *1          Yes (default)        Yes
PostgreSQL  Yes (default)   Yes                  Yes
DB2         -               -                    -
SQL Server  -               -                    -
MySQL       -               -                    -
MariaDB     -               -                    -
SAP Sybase  -               -                    -
HyperSQL    -               -                    -
H2          -               -                    -
Derby       -               -                    -

* 1 Meskipun Oracle 12c menerima status NOT DEFERRABLE batasan , itu sebenarnya mengabaikannya dan membuatnya berfungsi sebagaiDEFERRABLE INITIALLY IMMEDIATE .

Seperti yang Anda lihat, Oracle tidak mengimplementasikan tipe pertama (NOT DEFERRABLE ), dan itulah mengapa pengembang yang menggunakan Oracle (OP dalam kasus ini) mungkin bingung dan menganggap dua tipe pertama setara.

Yang cukup menarik adalah Oracle dan PostgreSQL memiliki tipe default yang berbeda. Mungkin itu memiliki implikasi kinerja.

The Impaler
sumber
4

TIDAK DAPAT DITERBITKAN - Anda tidak dapat mengubah pemeriksaan kendala, oracle memeriksanya setelah setiap pernyataan (yaitu langsung setelah penyisipan pernyataan).

DAPAT DIUBAH SEGERA AWAL - Oracle memeriksa kendala setelah setiap pernyataan. TAPI, Anda dapat mengubahnya menjadi setelah setiap transaksi (yaitu setelah komit):

set constraint pk_tab1 deferred;
hajili
sumber