Batasan kunci asing: Kapan menggunakan ON UPDATE dan ON DELETE

196

Saya sedang merancang skema database saya menggunakan MySQL Workbench, yang cukup keren karena Anda dapat melakukan diagram dan mengubahnya: P

Ngomong-ngomong, saya telah memutuskan untuk menggunakan InnoDB karena itu adalah dukungan Kunci Asing. Satu hal yang saya perhatikan adalah bahwa hal itu memungkinkan Anda untuk mengatur Pada Pembaruan dan pada opsi Hapus untuk kunci asing. Dapatkah seseorang menjelaskan di mana "Batasi", "Cascade" dan set null dapat digunakan dalam contoh sederhana?

Sebagai contoh, katakan saya memiliki usertabel yang mencakup a userID. Dan katakan saya memiliki tabel pesan messageyang banyak-ke-banyak yang memiliki 2 kunci asing (yang merujuk kunci utama yang sama, userIDdalam usertabel). Apakah mengatur opsi On Update dan On Delete berguna dalam kasus ini? Jika demikian, yang mana yang saya pilih? Jika ini bukan contoh yang baik, bisakah Anda memberikan contoh yang baik untuk menggambarkan bagaimana ini bisa berguna?

Terima kasih

meltuhamy
sumber

Jawaban:

485

Jangan ragu untuk memberikan batasan pada database. Anda pasti akan memiliki database yang konsisten, dan itulah salah satu alasan bagus untuk menggunakan database. Terutama jika Anda memiliki beberapa aplikasi yang memintanya (atau hanya satu aplikasi tetapi dengan mode langsung dan mode batch menggunakan sumber yang berbeda).

Dengan MySQL Anda tidak memiliki batasan lanjutan seperti yang Anda miliki di postgreSQL tetapi setidaknya batasan kunci asing cukup maju.

Kami akan mengambil contoh, tabel perusahaan dengan tabel pengguna yang berisi orang-orang dari perusahaan tesis ini

CREATE TABLE COMPANY (
     company_id INT NOT NULL,
     company_name VARCHAR(50),
     PRIMARY KEY (company_id)
) ENGINE=INNODB;

CREATE TABLE USER (
     user_id INT, 
     user_name VARCHAR(50), 
     company_id INT,
     INDEX company_id_idx (company_id),
     FOREIGN KEY (company_id) REFERENCES COMPANY (company_id) ON...
) ENGINE=INNODB;

Mari kita lihat klausa ON UPDATE :

  • PADA PEMBARUAN TERBARU : standar : jika Anda mencoba memperbarui company_id dalam tabel PERUSAHAAN mesin akan menolak operasi jika satu PENGGUNA setidaknya memiliki tautan pada perusahaan ini.
  • PADA PEMBARUAN TANPA TINDAKAN : sama dengan RESTRICT.
  • PADA UPDATE CASCADE : biasanya yang terbaik : jika Anda memperbarui company_id dalam satu baris tabel PERUSAHAAN, mesin akan memperbaruinya sesuai pada semua baris USER yang merujuk PERUSAHAAN ini (tetapi tidak ada pemicu yang diaktifkan pada tabel USER, peringatan). Mesin akan melacak perubahan untuk Anda, itu bagus.
  • PADA UPDATE SET NULL : jika Anda memperbarui company_id di baris tabel PERUSAHAAN, mesin akan mengatur USER yang terkait company_id ke NULL (harus tersedia di bidang USER company_id). Saya tidak dapat melihat hal yang menarik untuk dilakukan dengan itu pada pembaruan, tetapi saya mungkin salah.

Dan sekarang di sisi ON DELETE :

  • ON DELETE RESTRICT : default : jika Anda mencoba menghapus ID company_id di tabel PERUSAHAAN mesin akan menolak operasi jika satu PENGGUNA setidaknya memiliki tautan di perusahaan ini, dapat menyelamatkan hidup Anda.
  • HAPUS TANPA TINDAKAN : sama dengan RESTRICT
  • PADA HAPUS CASCADE : berbahaya : jika Anda menghapus baris perusahaan dalam tabel PERUSAHAAN mesin akan menghapus juga PENGGUNA terkait. Ini berbahaya tetapi dapat digunakan untuk melakukan pembersihan otomatis pada tabel sekunder (jadi itu bisa menjadi sesuatu yang Anda inginkan, tetapi tentu saja bukan untuk PERUSAHAAN <-> contoh PENGGUNA)
  • ON DELETE SET NULL : handful : jika Anda menghapus baris PERUSAHAAN, PENGGUNA yang terkait akan secara otomatis memiliki hubungan dengan NULL. Jika Null adalah nilai Anda untuk pengguna yang tidak memiliki perusahaan, ini bisa menjadi perilaku yang baik, misalnya mungkin Anda perlu menyimpan pengguna dalam aplikasi Anda, sebagai penulis beberapa konten, tetapi menghapus perusahaan itu bukan masalah bagi Anda.

biasanya default saya adalah: ON DELETE RESTRICT ON UPDATE CASCADE . dengan beberapa ON DELETE CASCADEuntuk tabel track (log - tidak semua log--, hal-hal seperti itu) dan ON DELETE SET NULLketika tabel master adalah 'atribut sederhana' untuk tabel yang berisi kunci asing, seperti tabel PEKERJAAN untuk tabel USER.

Edit

Sudah lama sejak saya menulis itu. Sekarang saya pikir saya harus menambahkan satu peringatan penting. MySQL memiliki satu batasan besar yang didokumentasikan dengan kaskade.Cascade tidak memicu pemicu . Jadi, jika Anda terlalu percaya diri pada mesin itu untuk menggunakan pemicu, Anda harus menghindari kendala kaskade.

Pemicu MySQL hanya aktif untuk perubahan yang dibuat ke tabel oleh pernyataan SQL. Mereka tidak mengaktifkan untuk perubahan dalam tampilan, atau oleh perubahan pada tabel yang dibuat oleh API yang tidak mengirimkan pernyataan SQL ke Server MySQL

==> Lihat di bawah hasil edit terakhir, banyak hal bergerak di domain ini

Pemicu tidak diaktifkan oleh tindakan kunci asing.

Dan saya tidak berpikir ini akan diperbaiki suatu hari nanti. Batasan kunci asing dikelola oleh penyimpanan InnoDb dan Pemicu dikelola oleh mesin MySQL SQL. Keduanya terpisah. Innodb adalah satu-satunya penyimpanan dengan manajemen kendala, mungkin mereka akan menambahkan pemicu langsung di mesin penyimpanan suatu hari, mungkin tidak.

Tapi saya punya pendapat sendiri tentang elemen mana yang harus Anda pilih antara implementasi pemicu yang buruk dan dukungan kendala kunci asing yang sangat berguna. Dan begitu Anda terbiasa dengan konsistensi basis data, Anda akan menyukai PostgreSQL.

12/2017-Memperbarui Edit ini tentang MySQL:

seperti yang dinyatakan oleh @IstiaqueAhmed dalam komentar, situasinya telah berubah pada subjek ini. Jadi ikuti tautannya dan periksa situasi terkini yang sebenarnya (yang mungkin berubah lagi di masa mendatang).

regilero
sumber
8
ON DELETE CASCADE : dangerous- ambil dengan sedikit garam.
onedaywhen
3
Anda harus berhati-hati dengan cascading, itu dapat mengunci sistem Anda jika banyak catatan perlu diubah. Hapus Cascde terutama harus dilihat dengan cermat sebelum menggunakan, sering Anda benar-benar ingin penghapusan tidak terjadi jika ada catatan anak. Saya tidak ingin pelanggan menghapus untuk menghapus data keuangan untuk oreders sebelumnya. Terkadang lebih baik untuk memastikan bahwa cacading tidak aktif dan menyediakan cara untuk menghapus catatan sebagai tidak aktif.
HLGEM
1
Dalam hal logika bisnis, ada satu kasus yang mungkin menarik SET NULLdalam ON UPDATE: memperbarui perusahaan merupakan pelepasan Perusahaan> Hubungan pengguna. Misalnya: jika perusahaan mengubah jenis bisnisnya, pengguna sebelumnya mungkin tidak lagi terkait dengan bisnis itu, karena itu NULLmungkin lebih disukai untuk indeks ini.
CPHPython
1
@regilero, sepertinya konten di tautan pertama Anda ( dev.mysql.com/doc/refman/5.6/en/triggers.html ) ke situs mysql telah berubah. Dikatakan This includes changes to base tables that underlie updatable viewsbukannya apa yang Anda tempel yaituThey do not activate for changes in views
Istiaque Ahmed
6
"Saya tidak ingin pelanggan menghapus untuk menghapus data keuangan untuk pesanan yang sebelumnya." Dalam situasi seperti itu, Anda mungkin masih membutuhkan data pelanggan. Desain Anda mungkin harus menandai pelanggan sebagai tidak aktif, tidak menghapus baris mereka dari basis data. Dalam praktiknya, sudah pengalaman profesional saya bahwa Anda sebenarnya sangat jarang ingin menghapus apa pun , lebih memilih untuk menandai tidak aktif secara default. Dalam kasus di mana secara permanen menghapus adalah ok, CASCADE DELETEumumnya juga oke, bahkan disukai. Saya tidak menganggapnya sangat berbahaya.
GrandOpener
3

Penambahan jawaban @MarkR - satu hal yang perlu diperhatikan adalah bahwa banyak kerangka kerja PHP dengan ORM tidak akan mengenali atau menggunakan pengaturan DB lanjutan (kunci asing, penghapusan cascading, kendala unik), dan ini dapat mengakibatkan perilaku yang tidak terduga.

Misalnya jika Anda menghapus catatan menggunakan ORM, dan Anda DELETE CASCADEakan menghapus catatan dalam tabel terkait, upaya ORM untuk menghapus catatan terkait ini (sering otomatis) akan menghasilkan kesalahan.

lxa
sumber
11
Itu akan menjadi alasan untuk tidak menggunakan ORM tertentu. Alat apa pun yang memiliki dukungan database yang buruk tidak dapat dipercaya. Kunci asing dan penghapusan cascading atau pembaruan adalah dasar-dasar db bukan konsep lanjutan dan tidak ada basis data nyata yang boleh dirancang tanpa batasan kunci asing!
HLGEM
Masalahnya adalah mereka melempar kesalahan. Apakah mungkin untuk RESTRICT DELETES tetapi memiliki mesin tidak menghasilkan kesalahan tetapi masih mempertahankan semantik? Saya ingin agar program saya tetap berjalan sementara pada saat yang sama melindungi data lain agar tidak dihapus.
TheRealChx101
2

Anda harus mempertimbangkan ini dalam konteks aplikasi. Secara umum, Anda harus mendesain aplikasi, bukan database (database hanya menjadi bagian dari aplikasi).

Pertimbangkan bagaimana aplikasi Anda harus menanggapi berbagai kasus.

Tindakan default adalah untuk membatasi (yaitu tidak mengizinkan) operasi, yang biasanya apa yang Anda inginkan karena mencegah kesalahan pemrograman bodoh. Namun, DELETE CASCADE juga bisa bermanfaat. Ini benar-benar tergantung pada aplikasi Anda dan bagaimana Anda ingin menghapus objek tertentu.

Secara pribadi, saya akan menggunakan InnoDB karena tidak membuang data Anda (lih. MyISAM, yang melakukannya), daripada karena memiliki kendala FK.

MarkR
sumber