Cara Menyimpan Data Historis

162

Beberapa rekan kerja dan saya berdebat tentang cara terbaik untuk menyimpan data historis. Saat ini, untuk beberapa sistem, saya menggunakan tabel terpisah untuk menyimpan data historis, dan saya menyimpan tabel asli untuk catatan aktif saat ini. Jadi, katakanlah saya punya meja FOO. Di bawah sistem saya, semua catatan aktif akan masuk FOO, dan semua catatan historis akan masuk FOO_Hist. Banyak bidang berbeda di FOO dapat diperbarui oleh pengguna, jadi saya ingin menyimpan akun yang akurat dari semua yang diperbarui. FOO_Hist memiliki bidang yang sama persis dengan FOO dengan pengecualian HIST_ID yang bertambah otomatis. Setiap kali FOO diperbarui, saya melakukan pernyataan menyisipkan ke FOO_Hist mirip dengan:insert into FOO_HIST select * from FOO where id = @id .

Rekan kerja saya mengatakan bahwa ini adalah desain yang buruk karena saya tidak boleh memiliki salinan tabel yang tepat karena alasan historis dan harus memasukkan catatan lain ke tabel aktif dengan bendera yang menunjukkan bahwa itu untuk tujuan historis.

Apakah ada standar untuk menangani penyimpanan data historis? Tampaknya bagi saya bahwa saya tidak ingin mengacaukan catatan aktif saya dengan semua catatan sejarah saya dalam tabel yang sama mengingat mungkin ada lebih dari satu juta catatan (saya berpikir jangka panjang).

Bagaimana Anda atau perusahaan Anda menangani ini?

Saya menggunakan MS SQL Server 2008, tetapi saya ingin tetap menyimpan jawaban generik dan sembarang dari DBMS mana pun.

Harun
sumber

Jawaban:

80

Mendukung data historis secara langsung dalam sistem operasional akan membuat aplikasi Anda jauh lebih kompleks daripada yang seharusnya. Secara umum, saya tidak akan merekomendasikan melakukannya kecuali Anda memiliki persyaratan yang sulit untuk memanipulasi versi historis dari catatan dalam sistem.

Jika Anda melihat lebih dekat, sebagian besar persyaratan untuk data historis termasuk dalam salah satu dari dua kategori:

  • Audit logging: Ini lebih baik dilakukan dengan tabel audit. Cukup mudah untuk menulis alat yang menghasilkan skrip untuk membuat tabel log audit dan pemicu dengan membaca metadata dari kamus data sistem. Jenis alat ini dapat digunakan untuk retrofit audit logging ke sebagian besar sistem. Anda juga dapat menggunakan subsistem ini untuk pengambilan data yang diubah jika Anda ingin mengimplementasikan gudang data (lihat di bawah).

  • Pelaporan historis: Pelaporan tentang kondisi historis, posisi 'as-at', atau pelaporan analitis seiring waktu. Dimungkinkan untuk memenuhi persyaratan pelaporan historis sederhana dengan menanyai tabel pencatatan audit dari jenis yang dijelaskan di atas. Jika Anda memiliki persyaratan yang lebih kompleks maka mungkin lebih ekonomis untuk mengimplementasikan data mart untuk pelaporan daripada mencoba dan mengintegrasikan sejarah langsung ke dalam sistem operasional.

    Dimensi yang berubah secara lambat sejauh ini merupakan mekanisme paling sederhana untuk melacak dan menanyakan keadaan historis dan sebagian besar pelacakan riwayat dapat diotomatisasi. Penangan generik tidak terlalu sulit untuk ditulis. Secara umum, pelaporan historis tidak harus menggunakan data terbaru, sehingga mekanisme penyegaran batch biasanya baik-baik saja. Ini membuat inti dan arsitektur sistem pelaporan Anda relatif sederhana.

Jika persyaratan Anda termasuk dalam salah satu dari dua kategori ini, Anda mungkin lebih baik tidak menyimpan data historis dalam sistem operasional Anda. Memisahkan fungsi historis ke dalam subsistem lain mungkin akan lebih sedikit usaha secara keseluruhan dan menghasilkan basis data transaksional dan audit / pelaporan yang bekerja jauh lebih baik untuk tujuan yang dimaksudkan.

ConcernedOfTunbridgeWells
sumber
Saya rasa saya mengerti apa yang Anda katakan. Jadi apa yang saya lakukan dengan tabel FOO_Hist saya benar-benar membuat tabel audit. Alih-alih menggunakan pemicu untuk memasukkan ke dalam tabel audit saat pembaruan, saya hanya menjalankan pernyataan di program. Apakah itu benar?
Aaron
6
Kurang lebih. Namun, lebih baik melakukan audit logging dengan pemicu ini; pemicu memastikan bahwa setiap perubahan (termasuk perbaikan data manual) dicatat dalam log audit. Jika Anda memiliki lebih dari 10-20 tabel untuk mengaudit, mungkin lebih cepat dari semuanya untuk membuat alat pembangkit pemicu. Jika lalu lintas disk untuk log audit adalah masalah, Anda dapat menempatkan tabel log audit pada set disk yang terpisah.
ConcernedOfTunbridgeWells
Ya, saya 100% setuju dengan itu. Terima kasih.
Aaron
40

Saya tidak berpikir ada cara standar tertentu untuk melakukannya tetapi saya pikir saya akan melemparkan metode yang mungkin. Saya bekerja di Oracle dan kerangka kerja aplikasi web in-house kami yang menggunakan XML untuk menyimpan data aplikasi.

Kami menggunakan sesuatu yang disebut model Master - Detail yang paling sederhana terdiri dari:

Master Table misalnya dipanggil Widgetssering hanya berisi ID. Akan sering berisi data yang tidak akan berubah seiring waktu / tidak historis.

Detail / Tabel Sejarah misalnya disebut Widget_Detailsmengandung setidaknya:

  • ID - kunci utama. Detail / ID historis
  • MASTER_ID - misalnya dalam hal ini disebut 'WIDGET_ID', ini adalah catatan FK ke Master
  • START_DATETIME - cap waktu yang menunjukkan awal baris basis data itu
  • END_DATETIME - cap waktu yang mengindikasikan akhir dari baris basis data itu
  • STATUS_CONTROL - kolom char tunggal menunjukkan status baris. 'C' menunjukkan saat ini, NULL atau 'A' akan menjadi historis / diarsipkan. Kami hanya menggunakan ini karena kami tidak dapat mengindeks pada END_DATETIME menjadi NULL
  • CREATED_BY_WUA_ID - menyimpan ID akun yang menyebabkan baris dibuat
  • XMLDATA - menyimpan data aktual

Jadi pada dasarnya, sebuah entitas dimulai dengan memiliki 1 baris di master dan 1 baris di detail. Detail memiliki tanggal akhir NULL dan STATUS_CONTROL dari 'C'. Ketika pembaruan terjadi, baris saat ini diperbarui untuk memiliki END_DATETIME dari waktu saat ini dan status_control diatur ke NULL (atau 'A' jika lebih disukai). Baris baru dibuat di tabel detail, masih ditautkan ke master yang sama, dengan status_control 'C', id orang yang membuat pembaruan dan data baru yang disimpan di kolom XMLDATA.

Ini adalah dasar dari model sejarah kita. Logika Buat / Perbarui ditangani dalam paket Oracle PL / SQL sehingga Anda cukup melewatkan fungsi ID saat ini, ID pengguna Anda dan data XML baru dan secara internal ia melakukan semua pembaruan / penyisipan baris untuk mewakili bahwa dalam model historis . Waktu mulai dan berakhir menunjukkan kapan baris dalam tabel aktif untuk.

Penyimpanan murah, kami biasanya tidak MENGHAPUS data dan lebih memilih untuk menjaga jejak audit. Ini memungkinkan kami untuk melihat seperti apa data kami pada waktu tertentu. Dengan mengindeks status_control = 'C' atau menggunakan Tampilan, kekacauan bukan masalah. Jelas pertanyaan Anda perlu memperhitungkan Anda harus selalu menggunakan versi saat ini (NULL end_datetime dan status_control = 'C').

Chris Cameron-Mills
sumber
Hai Chris, jika Anda melakukan itu, ID (kunci utama) harus diubah, bukan? bagaimana dengan relasional dengan tabel lain jika digunakan oleh yang lain?
projo
@projo ID pada tabel master Anda adalah PK dan secara konseptual "PK" untuk konsep apa pun yang Anda hadapi. ID pada tabel detail adalah PK untuk mengidentifikasi versi historis untuk master (yang merupakan kolom lain pada detail). Saat membentuk hubungan, Anda akan sering merujuk PK sebenarnya dari konsep Anda (yaitu ID pada tabel master Anda atau kolom MASTER_ID pada detail Anda) dan menggunakan STATUS_CONTROL = 'C' untuk memastikan Anda mendapatkan versi saat ini. Atau Anda dapat merujuk ID detail untuk menghubungkan sesuatu ke titik waktu tertentu.
Chris Cameron-Mills
+1 Saya telah menerapkan pola ini dengan sangat sukses pada beberapa proyek besar.
Three Value Logic
Kami menggunakan pendekatan yang sama. Tapi sekarang saya bertanya-tanya apakah lebih baik menyimpan hanya START_DATETIME dan jangan menyimpan END_DATETIME
bat_ventzi
Beberapa variasi dalam pengalaman saya. Jika entitas Anda "berakhir", yaitu diarsipkan atau dihapus maka Anda bisa saja tidak memiliki catatan detail dengan kontrol status 'C', yaitu tidak ada baris saat ini, meskipun Anda tidak akan tahu kapan itu terjadi. Atau, Anda dapat menetapkan end_datetime pada baris terakhir dan keberadaan baris 'berakhir' 'C' dapat menunjukkan entitas sekarang dihapus / diarsipkan. Akhirnya, Anda bisa mewakili ini melalui kolom lain, STATUS yang mungkin sudah Anda miliki.
Chris Cameron-Mills
15

Saya pikir pendekatan Anda sudah benar. Tabel historis harus merupakan salinan dari tabel utama tanpa indeks, pastikan Anda memiliki timestamp pembaruan di tabel juga.

Jika Anda mencoba pendekatan lain segera Anda akan menghadapi masalah:

  • overhead pemeliharaan
  • lebih banyak flag dalam pemilihan
  • pertanyaan perlambatan
  • pertumbuhan tabel, indeks
Alexander
sumber
7

Di SQL Server 2016 dan di atasnya , ada fitur baru yang disebut Tabel Temporal yang bertujuan untuk mengatasi tantangan ini dengan upaya minimal dari pengembang . Konsep tabel temporal mirip dengan Change Data Capture (CDC), dengan perbedaan bahwa tabel temporal telah mengabstraksi sebagian besar hal yang harus Anda lakukan secara manual jika Anda menggunakan CDC.

Benjamin
sumber
1

Hanya ingin menambahkan opsi yang saya mulai gunakan karena saya menggunakan Azure SQL dan beberapa tabel terlalu rumit untuk saya. Saya menambahkan pemicu sisipkan / perbarui / hapus di meja saya dan kemudian ubah sebelum / sesudah diubah menjadi json menggunakan fitur "FOR JSON AUTO".

 SET @beforeJson = (SELECT * FROM DELETED FOR JSON AUTO)
SET @afterJson = (SELECT * FROM INSERTED FOR JSON AUTO)

Itu mengembalikan representasi JSON untuk catatan sebelum / setelah perubahan. Saya kemudian menyimpan nilai-nilai itu di tabel sejarah dengan cap waktu kapan perubahan terjadi (saya juga menyimpan ID untuk catatan kekhawatiran saat ini). Dengan menggunakan proses serialisasi, saya dapat mengontrol bagaimana data ditimbun dalam kasus perubahan skema.

Saya belajar tentang ini dari tautan ini di sini

JakeHova
sumber
0

Anda bisa mempartisi tabel tidak?

"Tabel Partisi dan Strategi Indeks Menggunakan SQL Server 2008 Ketika tabel database tumbuh dalam ukuran hingga ratusan gigabyte atau lebih, itu bisa menjadi lebih sulit untuk memuat data baru, menghapus data lama, dan mempertahankan indeks. Hanya ukuran tabel semata-mata menyebabkan operasi seperti itu memakan waktu lebih lama. Bahkan data yang harus dimuat atau dihapus bisa sangat besar, membuat operasi INSERT dan DELETE di atas meja tidak praktis. Perangkat lunak database Microsoft SQL Server 2008 menyediakan partisi tabel untuk membuat operasi seperti itu lebih mudah dikelola. "

clyc
sumber
Ya, saya bisa mempartisi tabel, tetapi apakah itu standar saat berurusan dengan data historis? Haruskah data historis dimasukkan dalam tabel yang sama dengan data aktif? Ini adalah pertanyaan yang ingin saya diskusikan. Ini juga tidak sewenang-wenang karena berkaitan dengan SQL Server 2008.
Aaron
0

Pertanyaan sebenarnya adalah apakah Anda perlu menggunakan data historis dan data aktif bersama untuk pelaporan? Jika demikian simpan dalam satu tabel, partisi dan buat tampilan untuk catatan aktif untuk digunakan dalam kueri aktif. Jika Anda hanya perlu melihatnya sesekali (untuk meneliti masalah leagal atau semacamnya) maka letakkan di tabel terpisah.

HLGEM
sumber
2
Apakah lebih sulit untuk JOINdua tabel dalam beberapa laporan historis atau lebih sulit untuk memodifikasi setiap tabel tunggal menyisipkan / memperbarui / menghapus untuk mengetahui masalah sejarah? Sebenarnya, log audit bahkan akan memasukkan data saat ini dalam tabel sejarah, jadi tabel saat ini bahkan tidak diperlukan dalam laporan.
0

Pilihan lain adalah mengarsipkan data operasional berdasarkan [setiap hari | setiap jam | apa pun]. Sebagian besar mesin basis data mendukung ekstraksi data ke dalam arsip .

Pada dasarnya, idenya adalah membuat pekerjaan Windows atau CRON terjadwal itu

  1. menentukan tabel saat ini dalam database operasional
  2. memilih semua data dari setiap tabel ke dalam file CSV atau XML
  3. kompres data yang diekspor ke file ZIP, lebih disukai dengan cap waktu generasi dalam nama file untuk pengarsipan lebih mudah.

Banyak mesin basis data SQL datang dengan alat yang dapat digunakan untuk tujuan ini. Misalnya, saat menggunakan MySQL di Linux, perintah berikut dapat digunakan dalam pekerjaan CRON untuk menjadwalkan ekstraksi:

mysqldump --all-databases --xml --lock-tables=false -ppassword | gzip -c | cat > /media/bak/servername-$(date +%Y-%m-%d)-mysql.xml.gz
Michael
sumber
2
Ini sama sekali tidak sesuai untuk data historis karena jika seseorang mengubah nilai dan mengubahnya kembali dalam siklus arsip, pembaruan itu hilang. Juga tidak ada cara mudah untuk melihat perubahan pada satu entitas dari waktu ke waktu atau untuk mengembalikan entitas secara parsial.
Sgoettschkes
0

Saya Tahu posting lama ini tetapi Hanya ingin menambahkan beberapa poin. Standar untuk masalah seperti itu adalah yang paling sesuai untuk situasi tersebut. memahami perlunya penyimpanan seperti itu, dan potensi penggunaan data pelacakan historis / audit / perubahan sangat penting.

Audit (tujuan keamanan) : Gunakan tabel umum untuk semua tabel yang dapat diaudit. mendefinisikan struktur untuk menyimpan nama kolom, sebelum nilai dan setelah bidang nilai.

Arsip / Historis : untuk kasus-kasus seperti melacak alamat sebelumnya, nomor telepon dll. Membuat tabel terpisah FOO_HIST lebih baik jika skema tabel transaksi aktif Anda tidak berubah secara signifikan di masa mendatang (jika tabel riwayat Anda harus memiliki struktur yang sama). Jika Anda mengantisipasi normalisasi tabel, datatype mengubah penambahan / penghapusan kolom, simpan data historis Anda dalam format xml. mendefinisikan tabel dengan kolom berikut (ID, Tanggal, Versi Skema, XMLData). ini akan dengan mudah menangani perubahan skema. tetapi Anda harus berurusan dengan xml dan itu bisa menimbulkan tingkat komplikasi untuk pengambilan data.

Danny D
sumber