Apakah praktik yang buruk untuk memiliki kolom "catatan status" di tabel database?

12

Saya harus mengklarifikasi dulu bahwa kolom status tidak dimaksudkan untuk mencerminkan status item dunia nyata yang diwakili oleh catatan (baris) dalam tabel. Sebaliknya, itu dimaksudkan untuk menunjukkan status catatan itu sendiri.

Ini dapat sesederhana Aktif / Tidak Aktif atau rumit seperti Disetujui / Dihapus / Dikunci / Ditunda, dll. Status dapat disimpan pada kolom boolean / integer pendek atau kolom karakter tunggal, dengan pemetaan seperti true/ 1= Aktif atau A= Disetujui.

Ide dasarnya adalah untuk memiliki dukungan pemulihan recycle bin / trash-like dalam aplikasi (dan mensimulasikannya dalam database). Jika ada GUI front-end atau antarmuka lain yang seharusnya dapat membuat pengguna "menghapus" catatan, itu sebenarnya tidak menghapus catatan dalam tabel, tetapi hanya mengubah status catatan menjadi Tidak Aktif atau Dihapus. Ketika antarmuka mengambil catatan, selalu mendapat catatan yang hanya cocok dengan kondisi bahwa statusnya Aktif atau Disetujui.

Jika pengguna membuat kesalahan dan catatan "dihapus" (dalam perspektif pengguna) perlu dipulihkan, DBA dapat dengan mudah menambal catatan kembali menjadi Aktif atau Disetujui, yang akan lebih baik daripada mencari cadangan dan mudah-mudahan menemukan catatan asli sana. Atau antarmuka itu sendiri dapat membiarkan pengguna melihat catatan yang dihapus dalam tampilan terpisah, dan mengembalikannya sesuai kebutuhan, atau bahkan menghapusnya secara permanen (menghapus catatan aktual).

Pertanyaan saya:

  • Apakah ini praktik yang baik, atau praktik yang buruk?
  • Apakah itu memengaruhi normalisasi data?
  • Apa potensi jebakan?
  • Apakah ada metode alternatif untuk mencapai tujuan yang sama? (Lihat Catatan)
  • Bagaimana Anda bisa membuat database memberlakukan batasan unik pada data hanya untuk status tertentu (tetapi memungkinkan sejumlah duplikat untuk status lain)?
  • Mengapa basis data tidak menyediakan fitur "tempat sampah" atau pelacakan tabel / pemulihan secara native, sehingga kami dapat membiarkan antarmuka menghapus catatan aktual tanpa khawatir?

Catatan: Saya membaca tentang mempertahankan tabel sejarah yang terpisah tetapi tampaknya lebih buruk dalam hal penyimpanan dan harus menghasilkan pemicu dan menjaga pemicu tetap up-to-date dengan skema tabel yang dilacak.

ADTC
sumber
Masalah dengan batasan unik (yang sudah Anda sebutkan) adalah mengapa tabel sejarah sering lebih disukai - Anda dapat menyimpan batasan kunci unik pada tabel asli, dan jangan menambahkannya di tabel riwayat. Terlebih lagi, tabel riwayat terpisah memungkinkan lebih mudah untuk menggunakan opsi penyimpanan khusus (tergantung DB) untuk mereka, sehingga mereka seringkali lebih baik dalam hal penyimpanan, tidak lebih buruk. Saat Anda memiliki banyak tabel tersebut, pemicu dan tabel riwayat tidak boleh ditulis tangan, tetapi dibuat, yang akan memecahkan masalah bagaimana membuatnya tetap "terkini".
Doc Brown

Jawaban:

5

Saya tahu ini sebagai "Hapus Lunak"; hanya menandai catatan sebagai "dihapus", meskipun sebenarnya tidak.

Apakah ini praktik yang baik, atau praktik yang buruk?

Tergantung.
Jika ini adalah sesuatu yang sangat dibutuhkan pengguna Anda, maka itu mungkin hal yang baik. Namun, dalam sebagian besar kasus, saya berpendapat bahwa ini menambah [banyak] biaya tambahan untuk sedikit manfaat.

Apakah itu memengaruhi normalisasi data?

Tidak, tetapi itu akan mempengaruhi pengindeksan data Anda.
Pastikan Anda memasukkan kolom "dihapus" dalam indeks Anda, sehingga baris-baris ini dikecualikan sedini mungkin dalam kueri Anda.

Apa potensi jebakan?

Data Anda menjadi sedikit lebih kompleks. Segala sesuatu yang mendekati data perlu "tahu" tentang catatan ekstra ini, "tidak benar-benar ada". Atau, Anda harus membuat Tampilan pada tabel yang mengecualikan baris ini dan menggunakan tampilan ini di, katakanlah, Alat Pelaporan Pilihan Anda.

Basis data Anda mungkin bertambah besar. Jika Anda tidak benar-benar menghapus baris-baris ini maka mereka masih ada di sana, mengambil ruang. Ini mungkin atau mungkin bukan masalah, terutama karena Anda memasukkannya ke dalam indeks Anda, sehingga ruang yang mereka konsumsi bertambah banyak.

Apakah ada metode alternatif untuk mencapai tujuan yang sama? (Lihat Catatan)

Tidak juga, tidak.

Bagaimana Anda bisa membuat database memberlakukan batasan unik pada data hanya untuk status tertentu (tetapi memungkinkan sejumlah duplikat untuk status lain)?

Tidak mudah. Integritas Referensi Deklaratif (klausa kunci asing) adalah cara terbersih untuk menerapkan ini dan mudah untuk hal-hal seperti alat Pelaporan untuk mengambil pada aturan-aturan ini untuk menentukan hubungan antara tabel. Aturan tersebut berlaku untuk semua catatan, terlepas dari "status" (dan tidak ada jalan lain untuk itu).

Alternatifnya adalah dengan menggunakan Pemicu, potongan kode prosedural yang menegakkan integritas referensial antara tabel dan melakukan semua hal cerdas dan bersyarat yang Anda butuhkan. Itu bagus untuk kasus khusus Anda, tetapi sebagian besar manfaat Deklaratif RI keluar dari jendela - tidak ada hubungan yang terdeteksi secara eksternal antara tabel Anda; itu semua "tersembunyi" di pemicu.

Mengapa basis data tidak menyediakan fitur "tempat sampah" atau pelacakan tabel / pemulihan secara native, sehingga kami dapat membiarkan antarmuka menghapus catatan aktual tanpa khawatir?

Mengapa akan mereka?

Bagaimanapun, ini adalah basis data, bukan sistem file atau spreadsheet.

Apa yang mereka lakukan, mereka [dapat] lakukan dengan sangat, sangat baik.

Apa yang tidak mereka lakukan, mungkin belum banyak permintaan.

Phill W.
sumber
Jawaban yang bagus, tetapi ada opsi alternatif, misalnya memindahkan baris ke tabel cadangan dari mana Anda dapat memulihkannya. Tabel cadangan dapat memiliki indeks minimal. Ini meminimalkan masalah yang Anda catat dengan pendekatan yang ada (indeks yang lebih besar, potensi kebingungan untuk pengguna tabel dll), tetapi jelas menambahkan fakta bahwa Anda memiliki tabel lain untuk dipelihara (dan artinya entri sudah hilang, rujuk ke referensi kunci asing). Ada beberapa opsi lain - tetapi memang yang ada di pikiran adalah semua implementasi kustom, bukan sesuatu yang umum disediakan oleh setiap database SQL untuk kasus-kasus seperti itu.
Frank Hopkins
9

Itu praktik. Baik atau buruk itu sangat tergantung pada aplikasi Anda dan seberapa umum Anda benar-benar perlu / ingin melakukan "undelete". Saya akan sangat meragukan rencana untuk menempatkan semacam kolom dari setiap tabel dalam sistem - tampaknya sangat tidak mungkin bahwa Anda benar-benar akan repot-repot mengimplementasikan penghapusan dihapus pada setiap tabel dalam sistem. Dan itu membutuhkan implementasi - dalam sebagian besar kasus, Anda tidak membatalkan penghapusan satu baris pun dari satu tabel, Anda harus berjalan melalui tabel anak-anak membatalkan penghapusan baris dan memperbarui tabel terkait.

Untuk sebagian besar sisa pertanyaan, sangat tergantung pada implementasi. Sebagai contoh, Oracle menyediakan metode yang berbeda untuk melacak semua perubahan pada tabel - Flashback Data Archive (FDA juga dikenal sebagai Total Recall) menjadi pendekatan terbaru untuk mempertahankan sejarah penuh dari setiap versi baris dan pengarsipan dalam basis data untuk menerapkan pola hapus lunak. Basis data lain mungkin menyediakan cara lain untuk menerapkan pola. Bergantung pada basis data dan bagaimana Anda menerapkan penghapusan lunak, akan ada berbagai dampak pada kinerja, apakah dan bagaimana kendala dapat ditegakkan, dll. Jika kita berbicara tentang Oracle, Anda dapat melakukan banyak hal dengan indeks berbasis fungsi, misalnya , di SQL Server Anda sering dapat menggunakan indeks yang disaring untuk tujuan yang sama.

Gua Justin
sumber
Oracle Flashback adalah solusi tepat untuk apa yang saya inginkan. Sayang sekali itu milik Oracle.
ADTC
4

Sangat umum untuk menggunakan bidang "ditandai untuk dihapus" dalam sistem MRP / ERP.

Misalnya, orang mungkin ingin menandai bagian atau catatan inventaris yang tidak lagi dijual sebagai tidak aktif, tetapi masih ada pesanan luar biasa yang terkait dengannya. Melakukan penghapusan nyata pada catatan dapat memengaruhi pesanan yang belum dikirim, entri buku besar yang belum diposting, tabel riwayat yang tidak akan dibangun hingga akhir bulan, dll. Banyak sistem akan melarang penghapusan catatan kecuali melewati serangkaian validasi terhadap tabel lain. Jika Anda menghapus penghapusan melalui hubungan Anda, penghapusan yang sebenarnya bisa lebih destruktif.

Alih-alih, dengan menandai untuk dihapus, Anda menempatkan penanda niat yang jelas pada catatan dan kemudian tugas terjadwal dapat menghapus catatan jika memverifikasi bahwa semua tabel terkait tidak lagi merujuknya.

Kasus serupa dapat dibuat untuk fitur ini pada tabel pelanggan dan tabel "jangka panjang" lainnya. Bahkan masuk akal pada tabel yang lebih volatile seperti pesanan, meskipun nama bendera dapat menjadi sesuatu seperti "dikirim" atau "dibatalkan". Ini melayani fungsi yang sama: jangan hapus ini detik ini, tetapi gunakan itu sebagai bendera untuk program pembersihan sehingga ia mencoba untuk memvalidasi penghapusan catatan di masa mendatang.

Mike Mendukung Monica
sumber
3

Sebagai solusi alternatif, penggunaan event sourcing memungkinkan tujuan yang sama tanpa menyulitkan struktur tabel, meskipun itu membuat kode untuk memodifikasi data Anda sedikit lebih kompleks, karena Anda harus menulis modifikasi menjadi peristiwa yang dapat bertahan ke riwayat acara . Ini kemudian memungkinkan Anda untuk membuat ulang basis data seperti pada waktu tertentu, yang dapat menjadi fitur yang sangat berguna.

(Saya tidak percaya ini yang Anda maksud dengan "tabel sejarah", yang saya pikir Anda maksud hanya menyalin catatan yang dimodifikasi atau dihapus ke tabel lain sebelum mengubahnya)

Jules
sumber
Konsep yang menarik. Saya akan melihat bagaimana ini dapat diimplementasikan.
ADTC
1

Saya sering melihat dan menggunakan pola ini untuk kasus penggunaan ini:

  • metadata tempat Anda hanya ingin menampilkan nilai yang berlaku hari ini. Misalnya untuk memilih dari daftar pabrikan mobil dalam daftar drop-down di mana diaktifkan = 1 nilai tabel untuk ID, VALUE, ENABLED adalah 1, 'Ford', 1 dan 2, 'Edsel', 0, 3, 'Toyota' , 1 hanya memberi pilihan pada Ford dan Toyota
  • untuk sistem manajemen kasus di mana paradigma adalah bahwa suatu kasus hanya dapat berada dalam satu keadaan pada satu waktu. Dalam hal ini kolom sakelar disebut CURRENT dengan nilai 0 atau 1 ditegakkan oleh cek kendala. Ketika sebuah case berpindah dari satu state ke state yang lain, aplikasi memperbarui flag CURRENT dari state lama ke 0 dan yang baru ke 1

Masalahnya adalah untuk menegakkan integritas data jika lebih dari satu aplikasi atau layanan web menulis ke tabel. Bagaimana Anda memastikan bahwa untuk suatu kasus hanya ada satu keadaan saat ini? Seperti yang ditunjukkan oleh Justin Cave, ini dapat dilakukan di Oracle dengan membuat indeks virtual berdasarkan fungsi tetapi overhead tambahan ini untuk apa yang awalnya tampak konsep sederhana.

kevinsky
sumber
1

Ini praktik yang baik jika Anda berencana untuk menggunakan data Anda untuk pelaporan (aplikasi yang cukup besar perlu memiliki laporan).

Untuk mempercepat aplikasi Anda, Anda seharusnya tidak membiarkan alat pelaporan berjalan di basis data Anda. Karena itu, Anda perlu menyalin / menyinkronkan ke basis data lain.

Saya recordStatushanya menggunakan dua status ACTIVEatau CANCELLEDdalam kombinasi dengan lastUpdatedOncap waktu. Saya menggunakan recordStatusdaripada statusyang biasanya memiliki arti bisnis.

Ketika saya menyinkronkan database pelaporan dengan aplikasi, saya melakukan filter lastUpdatedOnuntuk mengetahui yang mana yang akan saya ganti di sisi pelaporan.

Di sisi pelaporan saya tidak akan memiliki recordStatusatau lastUpdatedOnbidang karena umumnya tidak akan dilaporkan. Karena itu ketika saya melihat CANCELLEDstatus saya akan menghapus catatan dari sisi pelaporan sehingga hanya memiliki catatan aktif.

Ini dapat diperluas ke jenis toko lain seperti arsip atau cadangan di mana sinkronisasi hampir penuh diperlukan. Namun, pelaporan adalah tujuan yang lebih umum.

Perhatikan contoh Anda dari Approved, New, PendingTIDAK ide yang baik untuk menempatkan sebagai bidang umum sebagai yang memiliki bisnis berarti harus pergi hanya ke tempat itu membuat bisnis akal yang bijaksana.

Sedangkan untuk terkunci, gunakan versionNoyang menyediakan kunci optimis untuk catatan Anda.

Pilihan lain alih-alih recordStatusadalah recordActivedan menyimpannya sebagai booleanyang membutuhkan lebih sedikit ruang dan lebih sedikit pengindeksan, tapi saya akan khawatir tentang kebutuhan di masa depan yang mungkin tidak Anda lihat sebelumnya.

Archimedes Trajano
sumber