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

Hapus orang tua jika tidak dirujuk oleh anak lain

Di PostgreSQL 9.1 atau lebih baru Anda dapat melakukannya dengan satu pernyataan menggunakan CTE pengubah data . Ini umumnya kurang rawan kesalahan. Ini meminimalkan kerangka waktu antara dua DELETE di mana kondisi balapan dapat menghasilkan hasil yang mengejutkan dengan operasi bersamaan:

WITH del_child AS (
    DELETE FROM child
    WHERE  child_id = 1
    RETURNING parent_id, child_id
    )
DELETE FROM parent p
USING  del_child x
WHERE  p.parent_id = x.parent_id
AND    NOT EXISTS (
   SELECT 1
   FROM   child c
   WHERE  c.parent_id = x.parent_id
   AND    c.child_id <> x.child_id   -- !
   );

SQL Fiddle.

Anak itu dihapus dalam hal apa pun. Saya mengutip manualnya:

Pernyataan pengubahan data dalam WITH dieksekusi tepat sekali, danselalu sampai selesai , terlepas dari apakah kueri utama membaca semua (atau memang ada) keluarannya. Perhatikan bahwa ini berbeda dari aturan untuk SELECT di WITH :seperti yang dinyatakan di bagian sebelumnya, eksekusi SELECT dijalankan hanya sejauh kueri utama menuntut keluarannya.

Induk hanya dihapus jika tidak memiliki lain anak-anak.
Perhatikan kondisi terakhir. Bertentangan dengan apa yang diharapkan, ini perlu, karena:

Sub-pernyataan dalam WITH dieksekusi bersamaan dengan satu sama lain dan dengan permintaan utama. Oleh karena itu, saat menggunakan pernyataan modifikasi data di WITH , urutan di mana pembaruan yang ditentukan benar-benar terjadi tidak dapat diprediksi. Semua pernyataan dieksekusi dengan snapshot yang sama (lihat Bab 13), sehingga mereka tidak dapat "melihat" efek satu sama lain pada tabel target.

Penekanan tebal milik saya.
Saya menggunakan nama kolom parent_id sebagai pengganti id non non-deskriptif .

Hilangkan kondisi balapan

Untuk menghilangkan kemungkinan kondisi balapan yang saya sebutkan di atas sepenuhnya , kunci baris induk pertama . Tentu saja, semua operasi serupa harus mengikuti prosedur yang sama untuk membuatnya bekerja.

WITH lock_parent AS (
   SELECT p.parent_id, c.child_id
   FROM   child  c
   JOIN   parent p ON p.parent_id = c.parent_id
   WHERE  c.child_id = 12              -- provide child_id here once
   FOR    NO KEY UPDATE                -- locks parent row.
   )
 , del_child AS (
   DELETE FROM child c
   USING  lock_parent l
   WHERE  c.child_id = l.child_id
   )
DELETE FROM parent p
USING  lock_parent l
WHERE  p.parent_id = l.parent_id
AND    NOT EXISTS (
   SELECT 1
   FROM   child c
   WHERE  c.parent_id = l.parent_id
   AND    c.child_id <> l.child_id   -- !
   );

Dengan cara ini hanya satu transaksi pada suatu waktu dapat mengunci orang tua yang sama. Jadi tidak mungkin terjadi banyak transaksi menghapus anak dari orang tua yang sama, masih melihat anak-anak lain dan menyelamatkan orang tua, sementara semua anak hilang setelahnya. (Pembaruan pada kolom non-kunci masih diperbolehkan dengan FOR NO KEY UPDATE .)

Jika kasus seperti itu tidak pernah terjadi atau Anda dapat menghadapinya (hampir tidak pernah) terjadi - kueri pertama lebih murah. Jika tidak, ini adalah jalur aman.

FOR NO KEY UPDATE diperkenalkan dengan Postgres 9.4. Detail dalam manual. Dalam versi yang lebih lama, gunakan kunci yang lebih kuat FOR UPDATE sebagai gantinya.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Inti Kerangka Entitas - Berisi apakah peka huruf besar-kecil atau tidak peka huruf besar-kecil?

  2. Antrian pekerjaan sebagai tabel SQL dengan banyak konsumen (PostgreSQL)

  3. Cara Keluar dari Utilitas Baris Perintah PostgreSQL (psql)

  4. pindahkan data dari satu tabel ke tabel lainnya, edisi postgresql

  5. Melakukan transaksi saat menjalankan Fungsi postgreql