Desain Basis Data untuk Revisi?

125

Kami memiliki persyaratan dalam proyek untuk menyimpan semua revisi (Riwayat Perubahan) untuk entitas dalam database. Saat ini kami memiliki 2 proposal yang dirancang untuk ini:

mis. untuk Entitas "Karyawan"

Desain 1:

-- Holds Employee Entity
"Employees (EmployeeId, FirstName, LastName, DepartmentId, .., ..)"

-- Holds the Employee Revisions in Xml. The RevisionXML will contain
-- all data of that particular EmployeeId
"EmployeeHistories (EmployeeId, DateModified, RevisionXML)"

Desain 2:

-- Holds Employee Entity
"Employees (EmployeeId, FirstName, LastName, DepartmentId, .., ..)"

-- In this approach we have basically duplicated all the fields on Employees 
-- in the EmployeeHistories and storing the revision data.
"EmployeeHistories (EmployeeId, RevisionId, DateModified, FirstName, 
      LastName, DepartmentId, .., ..)"

Apakah ada cara lain untuk melakukan hal ini?

Masalah dengan "Desain 1" adalah kita harus mengurai XML setiap kali ketika Anda perlu mengakses data. Ini akan memperlambat proses dan juga menambahkan beberapa batasan seperti kita tidak dapat menambahkan gabungan pada bidang data revisi.

Dan masalah dengan "Desain 2" adalah bahwa kita harus menduplikasi setiap bidang pada semua entitas (Kami memiliki sekitar 70-80 entitas yang ingin kami pertahankan revisi).

Ramesh Soni
sumber
1
FYI: Untuk berjaga-jaga, ini mungkin membantu .Sql server 2008 dan di atas memiliki teknologi yang menunjukkan sejarah perubahan di atas meja..kunjungi simple-talk.com/sql/learn-sql-server/… untuk mengetahui lebih banyak dan saya yakin DB's seperti Oracle juga akan memiliki sesuatu seperti ini.
Durai Amuthan.H
Harap diingat bahwa beberapa kolom dapat menyimpan XML atau JSON sendiri. Jika bukan itu masalahnya sekarang, itu bisa terjadi di masa depan. Lebih baik pastikan Anda tidak perlu menyarang data tersebut satu sama lain.
jakubiszon

Jawaban:

38
  1. Jangan tidak menempatkan semuanya dalam satu meja dengan atribut discriminator IsCurrent. Ini hanya menyebabkan masalah di telepon, membutuhkan kunci pengganti dan segala macam masalah lainnya.
  2. Desain 2 memang memiliki masalah dengan perubahan skema. Jika Anda mengubah tabel Karyawan, Anda harus mengubah tabel EmployeeHistories dan semua sprocs terkait yang menyertainya. Berpotensi menggandakan upaya skema Anda.
  3. Desain 1 bekerja dengan baik dan jika dilakukan dengan benar tidak memerlukan biaya yang besar dalam hal kinerja. Anda bisa menggunakan skema xml dan bahkan indeks untuk mengatasi kemungkinan masalah kinerja. Komentar Anda tentang penguraian xml valid tetapi Anda dapat dengan mudah membuat tampilan menggunakan xquery - yang dapat Anda sertakan dalam kueri dan bergabung dengannya. Sesuatu seperti ini...
CREATE VIEW EmployeeHistory
AS
, FirstName, , DepartmentId

SELECT EmployeeId, RevisionXML.value('(/employee/FirstName)[1]', 'varchar(50)') AS FirstName,

  RevisionXML.value('(/employee/LastName)[1]', 'varchar(100)') AS LastName,

  RevisionXML.value('(/employee/DepartmentId)[1]', 'integer') AS DepartmentId,

FROM EmployeeHistories 
Simon Munro
sumber
25
Mengapa Anda mengatakan tidak menyimpan semuanya dalam satu tabel dengan pemicu IsCurrent. Bisakah Anda mengarahkan saya ke beberapa contoh di mana ini akan menjadi masalah.
Nathan W
@Simon Munro Bagaimana dengan kunci primer atau kunci berkerumun? Kunci apa yang dapat kita tambahkan di tabel riwayat Desain 1 untuk membuat pencarian lebih cepat?
Gotqn
Saya menganggap SELECT * FROM EmployeeHistory WHERE LastName = 'Doe'hasil sederhana dalam pemindaian tabel penuh . Bukan ide terbaik untuk mengukur aplikasi.
Kaii
54

Saya pikir pertanyaan kunci untuk ditanyakan di sini adalah 'Siapa / Apa yang akan menggunakan sejarah'?

Jika sebagian besar untuk pelaporan / sejarah yang dapat dibaca manusia, kami telah menerapkan skema ini di masa lalu ...

Buat tabel yang disebut 'AuditTrail' atau sesuatu yang memiliki bidang berikut ...

[ID] [int] IDENTITY(1,1) NOT NULL,
[UserID] [int] NULL,
[EventDate] [datetime] NOT NULL,
[TableName] [varchar](50) NOT NULL,
[RecordID] [varchar](20) NOT NULL,
[FieldName] [varchar](50) NULL,
[OldValue] [varchar](5000) NULL,
[NewValue] [varchar](5000) NULL

Anda kemudian dapat menambahkan kolom 'LastUpdatedByUserID' ke semua tabel Anda yang harus disetel setiap kali Anda melakukan pembaruan / masukkan pada tabel.

Anda kemudian dapat menambahkan pemicu ke setiap tabel untuk menangkap setiap sisipan / pembaruan yang terjadi dan membuat entri di tabel ini untuk setiap bidang yang diubah. Karena tabel ini juga diberikan 'LastUpdateByUserID' untuk setiap pembaruan / masukkan, Anda dapat mengakses nilai ini di pemicu dan menggunakannya saat menambahkan ke tabel audit.

Kami menggunakan bidang RecordID untuk menyimpan nilai bidang kunci dari tabel yang sedang diperbarui. Jika itu adalah kunci gabungan, kami hanya melakukan penggabungan string dengan tanda '~' di antara bidang.

Saya yakin sistem ini mungkin memiliki kekurangan - untuk basis data yang banyak diperbarui, kinerjanya mungkin sangat bagus, tetapi untuk aplikasi web saya, kami mendapatkan lebih banyak pembacaan daripada penulisan dan tampaknya berkinerja cukup baik. Kami bahkan menulis sedikit utilitas VB.NET untuk secara otomatis menulis pemicu berdasarkan definisi tabel.

Hanya pemikiran saja!

Chris Roberts
sumber
5
Tidak perlu menyimpan NewValue, karena disimpan di tabel yang diaudit.
Petrus Theron
17
Sebenarnya, itu benar. Tetapi - ketika ada sejumlah perubahan pada bidang yang sama selama periode waktu tertentu, menyimpan nilai baru membuat pertanyaan seperti 'tunjukkan semua perubahan yang dibuat oleh Brian' jadi lebih mudah karena semua informasi tentang satu pembaruan disimpan di satu catatan. Hanya pemikiran saja!
Chris Roberts
1
Saya pikir sysnamemungkin tipe data yang lebih cocok untuk nama tabel dan kolom.
Sam
2
@ Sam menggunakan sysname tidak menambah nilai apa pun; bahkan mungkin membingungkan ... stackoverflow.com/questions/5720212/…
Jowen
19

Artikel History Tables di blog Programmer Database mungkin berguna - mencakup beberapa poin yang diangkat di sini dan membahas penyimpanan delta.

Edit

Dalam esai History Tables , penulis ( Kenneth Downs ), merekomendasikan mempertahankan tabel sejarah setidaknya tujuh kolom:

  1. Stempel waktu perubahan,
  2. Pengguna yang melakukan perubahan,
  3. Token untuk mengidentifikasi catatan yang telah diubah (di mana riwayat disimpan secara terpisah dari keadaan saat ini),
  4. Apakah perubahan itu menyisipkan, memperbarui, atau menghapus,
  5. Nilai lama,
  6. Nilai baru,
  7. Delta (untuk perubahan nilai numerik).

Kolom yang tidak pernah berubah, atau yang riwayatnya tidak diperlukan, tidak boleh dilacak di tabel riwayat untuk menghindari kembung. Menyimpan delta untuk nilai numerik dapat membuat kueri berikutnya lebih mudah, meskipun dapat diturunkan dari nilai lama dan baru.

Tabel histori harus aman, dengan pengguna non-sistem dicegah untuk memasukkan, memperbarui atau menghapus baris. Hanya pembersihan berkala yang didukung untuk mengurangi ukuran keseluruhan (dan jika diizinkan oleh use case).

Mark Streatfield
sumber
14

Kami telah menerapkan solusi yang sangat mirip dengan solusi yang disarankan Chris Roberts, dan itu bekerja dengan cukup baik bagi kami.

Satu-satunya perbedaan adalah kami hanya menyimpan nilai baru. Nilai lama setelah semua disimpan di baris riwayat sebelumnya

[ID] [int] IDENTITY(1,1) NOT NULL,
[UserID] [int] NULL,
[EventDate] [datetime] NOT NULL,
[TableName] [varchar](50) NOT NULL,
[RecordID] [varchar](20) NOT NULL,
[FieldName] [varchar](50) NULL,
[NewValue] [varchar](5000) NULL

Katakanlah Anda memiliki tabel dengan 20 kolom. Dengan cara ini Anda hanya perlu menyimpan kolom persis yang telah berubah daripada harus menyimpan seluruh baris.

Kjetil Watnedal
sumber
14

Hindari Desain 1; itu tidak sangat berguna sekali Anda harus misalnya rollback ke versi lama dari catatan - baik secara otomatis atau "secara manual" menggunakan konsol administrator.

Saya tidak benar-benar melihat kerugian dari Desain 2. Saya pikir yang kedua, tabel Riwayat harus berisi semua kolom yang ada di tabel, Catatan pertama. Misalnya dalam mysql Anda dapat dengan mudah membuat tabel dengan struktur yang sama dengan tabel lain ( create table X like Y). Dan, ketika Anda akan mengubah struktur tabel Records di database langsung Anda, Anda harus alter tabletetap menggunakan perintah - dan tidak ada upaya besar dalam menjalankan perintah ini juga untuk tabel History Anda.

Catatan

  • Tabel catatan hanya berisi revisi terbaru;
  • Tabel sejarah berisi semua revisi sebelumnya dari catatan di tabel Catatan;
  • Kunci utama tabel sejarah adalah kunci utama tabel Catatan dengan RevisionIdkolom yang ditambahkan ;
  • Pikirkan tentang bidang bantu tambahan seperti ModifiedBy- pengguna yang membuat revisi tertentu. Anda mungkin juga ingin memiliki bidang DeletedByuntuk melacak siapa yang menghapus revisi tertentu.
  • Pikirkan tentang apa yang DateModifiedseharusnya berarti - baik itu berarti di mana revisi khusus ini dibuat, atau itu akan berarti ketika revisi khusus ini digantikan oleh yang lain. Yang pertama mengharuskan bidang berada di tabel Catatan, dan tampaknya lebih intuitif pada pandangan pertama; solusi kedua namun tampaknya lebih praktis untuk catatan yang dihapus (tanggal ketika revisi khusus ini dihapus). Jika Anda mencari solusi pertama, Anda mungkin akan membutuhkan bidang kedua DateDeleted(hanya jika Anda membutuhkannya tentu saja). Tergantung Anda dan apa yang sebenarnya ingin Anda rekam.

Operasi dalam Desain 2 sangat sepele:

Memodifikasi
  • salin catatan dari tabel catatan ke tabel sejarah, berikan RevisionId baru (jika belum ada di tabel catatan), menangani DateModified (tergantung pada bagaimana Anda menafsirkannya, lihat catatan di atas)
  • lanjutkan dengan pembaruan normal catatan di tabel Catatan
Menghapus
  • lakukan persis sama seperti pada langkah pertama operasi Modify. Tangani DateModified / DateDeleted sesuai, tergantung pada interpretasi yang Anda pilih.
Batalkan penghapusan (atau kembalikan)
  • ambil revisi tertinggi (atau beberapa tertentu?) dari tabel History dan salin ke tabel Records
Daftar riwayat revisi untuk catatan tertentu
  • pilih dari tabel History dan tabel Records
  • pikirkan apa yang sebenarnya Anda harapkan dari operasi ini; mungkin akan menentukan informasi apa yang Anda butuhkan dari bidang DateModified / DateDeleted (lihat catatan di atas)

Jika Anda memilih Design 2, semua perintah SQL yang diperlukan untuk melakukannya akan sangat sangat mudah, demikian juga perawatannya! Mungkin, akan jauh lebih mudah jika Anda menggunakan kolom bantu ( RevisionId, DateModified) juga di tabel Catatan - untuk menjaga kedua tabel pada struktur yang sama persis (kecuali untuk kunci unik)! Ini akan memungkinkan untuk perintah SQL sederhana, yang akan toleran terhadap perubahan struktur data:

insert into EmployeeHistory select * from Employe where ID = XX

Jangan lupa menggunakan transaksi!

Sedangkan untuk penskalaan , solusi ini sangat efisien, karena Anda tidak mengubah data dari XML bolak-balik, hanya menyalin seluruh baris tabel - kueri yang sangat sederhana, menggunakan indeks - sangat efisien!

TMS
sumber
12

Jika Anda harus menyimpan riwayat, buat tabel bayangan dengan skema yang sama dengan tabel yang Anda lacak dan kolom 'Tanggal Revisi' dan 'Jenis Revisi' (mis. 'Hapus', 'pembaruan'). Tulis (atau hasilkan - lihat di bawah) seperangkat pemicu untuk mengisi tabel audit.

Cukup mudah untuk membuat alat yang akan membaca kamus data sistem untuk tabel dan menghasilkan skrip yang menciptakan tabel bayangan dan serangkaian pemicu untuk mengisinya.

Jangan mencoba menggunakan XML untuk ini, penyimpanan XML jauh lebih efisien daripada penyimpanan tabel database asli yang digunakan pemicu jenis ini.

ConcernedOfTunbridgeWells
sumber
3
+1 untuk kesederhanaan! Beberapa akan merekayasa berlebihan karena takut akan perubahan di kemudian hari, sementara sebagian besar waktu tidak ada perubahan yang sebenarnya terjadi! Selain itu, jauh lebih mudah untuk mengelola histori dalam satu tabel dan catatan aktual di tabel lain daripada memiliki semuanya dalam satu tabel (mimpi buruk) dengan beberapa bendera atau status. Ini disebut 'CIUMAN' dan biasanya akan membalas Anda dalam jangka panjang.
Jeach
+1 sepenuhnya setuju, persis apa yang saya katakan dalam jawaban saya ! Sederhana dan kuat!
TMS
8

Ramesh, saya terlibat dalam pengembangan sistem berdasarkan pendekatan pertama.
Ternyata menyimpan revisi sebagai XML mengarah ke pertumbuhan basis data yang sangat besar dan secara signifikan memperlambat segalanya.
Pendekatan saya adalah memiliki satu tabel per entitas:

Employee (Id, Name, ... , IsActive)  

di mana IsActive adalah tanda dari versi terbaru

Jika Anda ingin mengaitkan beberapa info tambahan dengan revisi, Anda bisa membuat tabel terpisah berisi info itu dan menautkannya dengan tabel entitas menggunakan relasi PK \ FK.

Dengan cara ini Anda dapat menyimpan semua versi karyawan dalam satu tabel. Kelebihan dari pendekatan ini:

  • Struktur basis data sederhana
  • Tidak ada konflik karena tabel menjadi append-only
  • Anda dapat mengembalikan ke versi sebelumnya hanya dengan mengubah bendera IsActive
  • Tidak perlu bergabung untuk mendapatkan riwayat objek

Perhatikan bahwa Anda harus mengizinkan kunci primer tidak unik.

aku
sumber
6
Saya akan menggunakan kolom "RevisionNumber" atau "RevisionDate" alih-alih atau sebagai tambahan untuk IsActive, sehingga Anda dapat melihat semua revisi secara berurutan.
Sklivvz
Saya akan menggunakan "parentRowId" karena itu memberi Anda akses mudah ke versi sebelumnya serta kemampuan untuk menemukan basis dan akhirnya dengan cepat.
chacham15
6

Cara saya melihat ini dilakukan di masa lalu adalah miliki

Employees (EmployeeId, DateModified, < Employee Fields > , boolean isCurrent );

Anda tidak pernah "memperbarui" pada tabel ini (kecuali untuk mengubah validitas isCurrent), cukup masukkan baris baru. Untuk EmployeeId yang diberikan, hanya 1 baris yang dapat memiliki isCurrent == 1.

Kompleksitas mempertahankan ini dapat disembunyikan oleh pandangan dan "bukan" pemicu (dalam oracle, saya anggap hal serupa RDBMS lainnya), Anda bahkan dapat pergi ke tampilan terwujud jika tabel terlalu besar dan tidak dapat ditangani oleh indeks) .

Metode ini ok, tetapi Anda bisa berakhir dengan beberapa pertanyaan kompleks.

Secara pribadi, saya sangat menyukai cara Desain 2 Anda untuk melakukannya, yang merupakan cara saya melakukannya di masa lalu juga. Sederhana untuk dipahami, mudah diimplementasikan, dan mudah dipelihara.

Itu juga menciptakan sangat sedikit overhead untuk database dan aplikasi, terutama ketika melakukan permintaan baca, yang kemungkinan akan Anda lakukan 99% dari waktu.

Ini juga akan cukup mudah untuk secara otomatis membuat tabel sejarah dan pemicu untuk dipelihara (dengan asumsi itu akan dilakukan melalui pemicu).

Matthew Watson
sumber
4

Revisi data adalah aspek konsep ' valid-time ' dari Database Temporal. Banyak penelitian telah dilakukan, dan banyak pola dan pedoman telah muncul. Saya menulis balasan panjang dengan banyak referensi untuk pertanyaan ini bagi mereka yang tertarik.

Henrik Gustafsson
sumber
4

Saya akan berbagi dengan Anda desain saya dan ini berbeda dari kedua desain Anda karena membutuhkan satu tabel untuk setiap jenis entitas. Saya menemukan cara terbaik untuk mendeskripsikan desain database adalah melalui ERD, ini milik saya:

masukkan deskripsi gambar di sini

Dalam contoh ini kami memiliki entitas bernama karyawan . tabel pengguna menyimpan catatan pengguna Anda dan entitas dan entitas_revision adalah dua tabel yang menyimpan riwayat revisi untuk semua jenis entitas yang akan Anda miliki di sistem Anda. Begini cara desain ini bekerja:

Dua bidang dari entity_id dan revision_id

Setiap entitas di sistem Anda akan memiliki id entitas unik sendiri. Entitas Anda mungkin melalui revisi tetapi entity_idnya akan tetap sama. Anda harus menyimpan id entitas ini di meja karyawan Anda (sebagai kunci asing). Anda juga harus menyimpan tipe entitas Anda di tabel entitas (mis. 'Karyawan'). Sekarang seperti untuk revision_id, seperti namanya, itu melacak revisi entitas Anda. Cara terbaik yang saya temukan untuk ini adalah dengan menggunakan employee_id sebagai revision_id Anda. Ini berarti Anda akan memiliki id revisi duplikat untuk berbagai jenis entitas, tetapi ini bukan masalah bagi saya (saya tidak yakin dengan kasus Anda). Satu-satunya catatan penting yang harus dibuat adalah bahwa kombinasi dari entity_id dan revision_id harus unik.

Ada juga bidang keadaan dalam tabel entity_revision yang menunjukkan keadaan revisi. Hal ini dapat memiliki salah satu dari tiga negara: latest, obsoleteatau deleted(tidak bergantung pada tanggal revisi membantu Anda banyak untuk meningkatkan pertanyaan Anda).

Satu catatan terakhir tentang revision_id, saya tidak membuat kunci asing yang menghubungkan employee_id ke revision_id karena kami tidak ingin mengubah tabel entitas_revision untuk setiap jenis entitas yang mungkin kami tambahkan di masa depan.

INSERSI

Untuk setiap karyawan yang ingin Anda masukkan ke dalam basis data, Anda juga akan menambahkan catatan ke entitas dan entitas_revisi . Dua catatan terakhir ini akan membantu Anda melacak oleh siapa dan kapan catatan telah dimasukkan ke dalam basis data.

MEMPERBARUI

Setiap pembaruan untuk catatan karyawan yang ada akan diimplementasikan sebagai dua sisipan, satu di tabel karyawan dan satu di entitas_revision. Yang kedua akan membantu Anda untuk mengetahui oleh siapa dan kapan catatan telah diperbarui.

PENGHAPUSAN

Untuk menghapus karyawan, catatan dimasukkan ke entitas_revision yang menyatakan penghapusan dan dilakukan.

Seperti yang Anda lihat dalam desain ini tidak ada data yang pernah diubah atau dihapus dari database dan yang lebih penting setiap jenis entitas hanya membutuhkan satu tabel. Secara pribadi saya menemukan desain ini sangat fleksibel dan mudah dikerjakan. Tetapi saya tidak yakin tentang Anda karena kebutuhan Anda mungkin berbeda.

[MEMPERBARUI]

Setelah mendukung partisi dalam versi MySQL baru, saya percaya desain saya juga dilengkapi dengan salah satu pertunjukan terbaik juga. Satu dapat mempartisi entitytabel menggunakan typebidang sementara partisi entity_revisionmenggunakan statebidangnya. Ini akan meningkatkan SELECTpertanyaan sejauh ini sambil menjaga desain tetap sederhana dan bersih.

Mehran
sumber
3

Jika memang jejak audit adalah yang Anda butuhkan, saya akan condong ke solusi tabel audit (lengkap dengan salinan denormalized dari kolom penting di tabel lain, misalnya, UserName). Perlu diingat, pengalaman pahit itu mengindikasikan bahwa satu meja audit akan menjadi hambatan besar di jalan; mungkin sepadan dengan upaya untuk membuat tabel audit individual untuk semua tabel yang diaudit.

Jika Anda perlu melacak versi historis (dan / atau masa depan) yang sebenarnya, maka solusi standar adalah untuk melacak entitas yang sama dengan beberapa baris menggunakan beberapa kombinasi nilai awal, akhir, dan durasi. Anda dapat menggunakan tampilan untuk membuat mengakses nilai saat ini nyaman. Jika ini adalah pendekatan yang Anda ambil, Anda dapat mengalami masalah jika referensi data versi Anda bisa berubah tetapi data tidak berversi.

Hank Gay
sumber
3

Jika Anda ingin melakukan yang pertama, Anda mungkin ingin menggunakan XML untuk tabel Karyawan juga. Sebagian besar basis data yang lebih baru memungkinkan Anda melakukan kueri ke dalam bidang XML sehingga ini tidak selalu menjadi masalah. Dan mungkin lebih mudah untuk memiliki satu cara untuk mengakses data karyawan terlepas dari apakah itu versi terbaru atau versi sebelumnya.

Saya akan mencoba pendekatan kedua. Anda bisa menyederhanakan ini dengan hanya memiliki satu tabel Karyawan dengan bidang DateModified. EmployeeId + DateModified akan menjadi kunci utama dan Anda dapat menyimpan revisi baru dengan hanya menambahkan baris. Dengan cara ini, pengarsipan versi yang lebih lama dan mengembalikan versi dari arsip juga lebih mudah.

Cara lain untuk melakukan ini bisa menjadi model datavault oleh Dan Linstedt. Saya melakukan proyek untuk biro statistik Belanda yang menggunakan model ini dan itu bekerja dengan sangat baik. Tapi saya tidak berpikir itu langsung berguna untuk penggunaan basis data sehari-hari. Anda mungkin mendapatkan beberapa ide dari membaca makalahnya.

Mendelt
sumber
2

Bagaimana tentang:

  • Identitas pegawai
  • DateModified
    • dan / atau nomor revisi, tergantung pada bagaimana Anda ingin melacaknya
  • DiubahByUSerId
    • ditambah informasi lain yang ingin Anda lacak
  • Bidang karyawan

Anda membuat kunci utama (EmployeeId, DateModified), dan untuk mendapatkan catatan "saat ini" Anda cukup memilih MAX (DateModified) untuk setiap karyawanid. Menyimpan IsCurrent adalah ide yang sangat buruk, karena pertama-tama, dapat dihitung, dan kedua, terlalu mudah bagi data untuk keluar dari sinkronisasi.

Anda juga dapat membuat tampilan yang hanya berisi daftar catatan terbaru, dan sebagian besar menggunakannya saat bekerja di aplikasi Anda. Yang menyenangkan tentang pendekatan ini adalah Anda tidak memiliki duplikat data, dan Anda tidak harus mengumpulkan data dari dua tempat berbeda (saat ini di Karyawan, dan diarsipkan di EmployeesHistory) untuk mendapatkan semua sejarah atau kembalikan, dll) .

gregmac
sumber
Kelemahan tentang pendekatan ini adalah bahwa tabel akan tumbuh lebih cepat daripada jika Anda menggunakan dua tabel.
cdmckay
2

Jika Anda ingin mengandalkan data riwayat (untuk alasan pelaporan), Anda harus menggunakan struktur seperti ini:

// Holds Employee Entity
"Employees (EmployeeId, FirstName, LastName, DepartmentId, .., ..)"

// Holds the Employee revisions in rows.
"EmployeeHistories (HistoryId, EmployeeId, DateModified, OldValue, NewValue, FieldName)"

Atau solusi global untuk aplikasi:

// Holds Employee Entity
"Employees (EmployeeId, FirstName, LastName, DepartmentId, .., ..)"

// Holds all entities revisions in rows.
"EntityChanges (EntityName, EntityId, DateModified, OldValue, NewValue, FieldName)"

Anda dapat menyimpan revisi Anda juga dalam XML, maka Anda hanya memiliki satu catatan untuk satu revisi. Ini akan terlihat seperti:

// Holds Employee Entity
"Employees (EmployeeId, FirstName, LastName, DepartmentId, .., ..)"

// Holds all entities revisions in rows.
"EntityChanges (EntityName, EntityId, DateModified, XMLChanges)"
dariol
sumber
1
Lebih baik: gunakan sumber acara :)
dariol
1

Kami memiliki persyaratan serupa, dan yang kami temukan adalah sering kali pengguna hanya ingin melihatnya apa yang telah diubah, belum tentu mengembalikan perubahan apa pun.

Saya tidak yakin kasus penggunaan Anda, tetapi yang kami lakukan adalah membuat dan mengaudit tabel yang diperbarui secara otomatis dengan perubahan pada entitas bisnis, termasuk nama yang ramah dari setiap referensi dan enumerasi kunci asing.

Setiap kali pengguna menyimpan perubahan mereka, kami memuat ulang objek lama, menjalankan perbandingan, mencatat perubahan, dan menyimpan entitas (semua dilakukan dalam satu transaksi basis data jika ada masalah).

Ini tampaknya bekerja sangat baik untuk pengguna kami dan menyelamatkan kami dari sakit kepala memiliki tabel audit yang benar-benar terpisah dengan bidang yang sama dengan entitas bisnis kami.

mattruma
sumber
0

Sepertinya Anda ingin melacak perubahan pada entitas tertentu dari waktu ke waktu, mis. ID 3, "bob", "123 main street", lalu ID 3 lainnya, "bob" "234 elm st", dan seterusnya, pada dasarnya bisa untuk memuntahkan riwayat revisi yang menunjukkan setiap alamat "bob" telah ada di.

Cara terbaik untuk melakukan ini adalah memiliki bidang "saat ini" pada setiap catatan, dan (mungkin) cap waktu atau FK ke tabel tanggal / waktu.

Sisipan kemudian harus mengatur "is current" dan juga unset the "is current" pada catatan "is current" sebelumnya. Kueri harus menentukan "sekarang", kecuali jika Anda ingin semua riwayat.

Ada beberapa perubahan lebih lanjut untuk ini jika tabelnya sangat besar, atau sejumlah besar revisi diharapkan, tetapi ini adalah pendekatan yang cukup standar.

Steve Moon
sumber