Untuk menjawab dengan benar pertanyaan ini, Anda harus terlebih dahulu memutuskan: Apa arti "hapus" dalam konteks sistem / aplikasi ini?
Untuk menjawab bahwa pertanyaan, Anda perlu menjawab pertanyaan lain: Mengapa yang catatan yang dihapus?
Ada sejumlah alasan bagus mengapa pengguna mungkin perlu menghapus data. Biasanya saya menemukan bahwa hanya ada satu alasan (per tabel) mengapa penghapusan mungkin diperlukan. Beberapa contoh adalah:
- Untuk mendapatkan kembali ruang disk;
- Penghapusan keras diperlukan sesuai dengan retensi / kebijakan privasi;
- Data yang salah / putus asa, lebih mudah dihapus dan dibuat ulang daripada diperbaiki.
- The Mayoritas dari baris yang dihapus, misalnya log meja terbatas pada catatan X / hari.
Ada juga beberapa alasan yang sangat buruk untuk penghapusan-keras (lebih lanjut tentang alasan untuk ini nanti):
- Untuk memperbaiki kesalahan kecil. Ini biasanya menggarisbawahi kemalasan pengembang dan UI yang bermusuhan.
- Untuk "membatalkan" transaksi (mis. Faktur yang seharusnya tidak pernah ditagih).
- Karena kamu bisa .
Mengapa, Anda bertanya, apakah ini benar-benar masalah besar? Apa yang salah dengan ole bagus DELETE
?
- Dalam sistem apa pun yang bahkan terkait dengan uang, penghapusan-keras melanggar segala macam harapan akuntansi, bahkan jika dipindahkan ke tabel arsip / batu nisan. Cara yang benar untuk menangani ini adalah peristiwa retroaktif .
- Tabel arsip memiliki kecenderungan untuk menyimpang dari skema langsung. Jika Anda lupa bahkan satu kolom atau kaskade yang baru ditambahkan, Anda baru saja kehilangan data itu secara permanen.
- Penghapusan sulit bisa menjadi operasi yang sangat mahal, terutama dengan kaskade . Banyak orang tidak menyadari bahwa cascading lebih dari satu level (atau dalam beberapa kasus cascading apa pun , tergantung pada DBMS) akan menghasilkan operasi tingkat rekor alih-alih mengatur operasi.
- Penghapusan yang sering dan berulang-ulang mempercepat proses fragmentasi indeks.
Jadi, hapus lunak lebih baik, bukan? Tidak terlalu:
- Menyiapkan kaskade menjadi sangat sulit. Anda hampir selalu berakhir dengan apa yang tampak oleh klien sebagai baris yatim.
- Anda hanya bisa melacak satu penghapusan. Bagaimana jika baris dihapus dan dihapus beberapa kali?
- Baca kinerja menderita, meskipun hal ini dapat dikurangi dengan partisi, tampilan, dan / atau indeks yang difilter.
- Seperti yang diisyaratkan sebelumnya, mungkin sebenarnya ilegal di beberapa skenario / yurisdiksi.
Yang benar adalah kedua pendekatan ini salah. Menghapus itu salah. Jika Anda benar-benar mengajukan pertanyaan ini maka itu berarti Anda memodelkan keadaan saat ini alih-alih transaksi. Ini adalah praktik buruk, buruk di basis data.
Udi Dahan menulis tentang ini di Don't Delete - Just Don't . Ada selalu semacam tugas, transaksi, aktivitas , atau (jangka pilihan saya) acara yang benar-benar mewakili "delete". Tidak apa-apa jika Anda kemudian ingin melakukan denormalisasi ke dalam tabel "kondisi saat ini" untuk kinerja, tetapi lakukan itu setelah Anda berhasil menyelesaikan model transaksional, bukan sebelumnya.
Dalam hal ini Anda memiliki "pengguna". Pengguna pada dasarnya adalah pelanggan. Pelanggan memiliki hubungan bisnis dengan Anda. Hubungan itu tidak hilang begitu saja karena mereka membatalkan akun mereka. Apa yang sebenarnya terjadi adalah:
- Pelanggan membuat akun
- Pelanggan membatalkan akun
- Pelanggan memperbarui akun
- Pelanggan membatalkan akun
- ...
Dalam setiap kasus, itu adalah pelanggan yang sama , dan mungkin akun yang sama (yaitu setiap pembaruan akun adalah perjanjian layanan baru). Jadi mengapa Anda menghapus baris? Ini sangat mudah untuk dimodelkan:
+-----------+ +-------------+ +-----------------+
| Account | --->* | Agreement | --->* | AgreementStatus |
+-----------+ +-------------+ +----------------+
| Id | | Id | | AgreementId |
| Name | | AccountId | | EffectiveDate |
| Email | | ... | | StatusCode |
+-----------+ +-------------+ +-----------------+
Itu dia. Itu semua yang ada untuk itu. Anda tidak perlu menghapus apa pun. Di atas adalah desain yang cukup umum yang mengakomodasi tingkat fleksibilitas yang baik tetapi Anda dapat menyederhanakannya sedikit; Anda mungkin memutuskan bahwa Anda tidak memerlukan level "Perjanjian" dan cukup "Account" masuk ke tabel "AccountStatus".
Jika sering dibutuhkan dalam aplikasi Anda untuk mendapatkan daftar perjanjian / akun aktif maka itu adalah (sedikit) permintaan rumit, tapi itulah gunanya tampilan:
CREATE VIEW ActiveAgreements AS
SELECT agg.Id, agg.AccountId, acc.Name, acc.Email, s.EffectiveDate, ...
FROM AgreementStatus s
INNER JOIN Agreement agg
ON agg.Id = s.AgreementId
INNER JOIN Account acc
ON acc.Id = agg.AccountId
WHERE s.StatusCode = 'ACTIVE'
AND NOT EXISTS
(
SELECT 1
FROM AgreementStatus so
WHERE so.AgreementId = s.AgreementId
AND so.EffectiveDate > s.EffectiveDate
)
Dan kamu sudah selesai. Sekarang Anda memiliki sesuatu dengan semua manfaat penghapusan-lunak tetapi tidak ada kekurangan:
- Catatan yatim adalah masalah karena semua catatan terlihat setiap saat; Anda cukup memilih dari tampilan yang berbeda kapan pun diperlukan.
- "Menghapus" biasanya merupakan operasi yang sangat murah - hanya memasukkan satu baris ke tabel acara.
- Tidak pernah ada kemungkinan kehilangan sejarah apapun, pernah , tidak peduli seberapa buruk Anda mengacaukan.
- Anda masih dapat menghapus akun dengan susah payah jika perlu (misalnya karena alasan privasi), dan merasa nyaman dengan pengetahuan bahwa penghapusan akan terjadi dengan bersih dan tidak mengganggu bagian lain dari aplikasi / basis data.
Satu-satunya masalah yang tersisa untuk ditangani adalah masalah kinerja. Dalam banyak kasus itu ternyata bukan masalah karena indeks berkerumun di AgreementStatus (AgreementId, EffectiveDate)
- sangat sedikit I / O mencari terjadi di sana. Tetapi jika itu pernah menjadi masalah, ada cara untuk menyelesaikannya, menggunakan pemicu, indeks / tampilan terwujud, peristiwa tingkat aplikasi, dll.
Jangan khawatir tentang kinerja terlalu dini - itu lebih penting untuk mendapatkan desain yang benar, dan "benar" dalam hal ini berarti menggunakan database cara database dimaksudkan untuk digunakan, sebagai sistem transaksional .