DATA-MODELING DATABASE-DESIGN TEMPORAL-DATA AUDIT-TRAIL DATA-INTEGRITY SOFTWARE-ARCHITECTURE BACKEND-DEVELOPMENT SQL DATA-MANAGEMENT BEST-PRACTICES

Memodelkan Data Bi-temporal: Melacak Perubahan Data Berdasarkan Waktu Transaksi dan Waktu Validitas

⏱️ 11 menit baca
👨‍💻

Memodelkan Data Bi-temporal: Melacak Perubahan Data Berdasarkan Waktu Transaksi dan Waktu Validitas

Pernahkah Anda berhadapan dengan data yang terus berubah seiring waktu? Misalnya, harga produk yang naik turun, status pesanan yang diperbarui, atau kebijakan diskon yang berlaku pada periode tertentu. Bagi developer, mengelola histori perubahan data ini seringkali menjadi tantangan. Kita ingin tahu tidak hanya apa nilai data saat ini, tapi juga kapan nilai tersebut berlaku dan kapan kita mencatat perubahan itu di sistem.

Inilah inti dari pemodelan data bi-temporal. Ini adalah fondasi kuat untuk membangun aplikasi yang membutuhkan auditabilitas tinggi, analisis historis yang akurat, atau bahkan kemampuan “time travel” untuk melihat data di masa lalu.

Artikel ini akan membawa Anda menyelami konsep bi-temporal, mengapa penting, bagaimana memodelkannya, dan cara melakukan query untuk membuka kekuatan histori data Anda. Mari kita mulai!

1. Pendahuluan: Mengapa Histori Data Lebih Kompleks dari Sekadar updated_at?

Banyak aplikasi hanya menyimpan data saat ini dan mungkin menambahkan kolom created_at serta updated_at. Ini cukup untuk banyak kasus sederhana, tapi bagaimana jika Anda perlu menjawab pertanyaan seperti:

Hanya dengan updated_at, Anda mungkin hanya tahu kapan record terakhir diperbarui, bukan periode validitas suatu nilai atau kapan sistem mengetahui informasi tersebut. Di sinilah pemodelan bi-temporal bersinar.

Bi-temporal data memungkinkan kita untuk melacak dua dimensi waktu secara bersamaan, memberikan gambaran yang jauh lebih kaya dan akurat tentang evolusi data.

2. Memahami Dua Dimensi Waktu: Valid Time & Transaction Time

Konsep kunci dari bi-temporal adalah membedakan antara dua jenis waktu:

📌 A. Valid Time (Waktu Validitas)

📌 B. Transaction Time (Waktu Transaksi)

💡 Analogi Sederhana: Bayangkan sebuah koran.

3. Manfaat Memodelkan Data Bi-temporal

Mengadopsi pemodelan bi-temporal membawa sejumlah keuntungan penting:

4. Pola Dasar Pemodelan Skema Bi-temporal

Untuk mengimplementasikan bi-temporal di database relasional (misalnya PostgreSQL, MySQL), kita biasanya menambahkan empat kolom waktu ke tabel data. Mari kita gunakan contoh harga produk.

CREATE TABLE ProductPriceHistory (
    id SERIAL PRIMARY KEY,
    product_id INT NOT NULL,
    price DECIMAL(10, 2) NOT NULL,
    currency VARCHAR(3) NOT NULL DEFAULT 'IDR',

    valid_from TIMESTAMP WITH TIME ZONE NOT NULL,
    valid_to TIMESTAMP WITH TIME ZONE, -- NULL berarti berlaku hingga sekarang/masa depan
    
    transaction_from TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    transaction_to TIMESTAMP WITH TIME ZONE -- NULL berarti record ini adalah versi yang 'saat ini diketahui' oleh sistem
);

Penjelasan Kolom Waktu:

Contoh Data:

Misalkan kita punya produk PROD001.

idproduct_idpricecurrencyvalid_fromvalid_totransaction_fromtransaction_to
1PROD001100.00IDR2023-01-01 00:00:002023-01-31 23:59:592022-12-20 10:00:00NULL
2PROD001120.00IDR2023-02-01 00:00:00NULL2023-01-25 09:30:00NULL

Record 1 menunjukkan harga Rp 100.000 berlaku dari 1 Jan hingga 31 Jan 2023. Informasi ini dicatat oleh sistem pada 20 Des 2022, dan ini adalah versi terkini yang diketahui sistem untuk periode validitas tersebut. Record 2 menunjukkan harga Rp 120.000 berlaku dari 1 Feb 2023 dan seterusnya. Informasi ini dicatat oleh sistem pada 25 Jan 2023.

⚠️ Penting: Untuk setiap product_id, tidak boleh ada overlap pada valid_time atau transaction_time untuk record yang aktif (yaitu transaction_to IS NULL). Ini bisa diatur dengan constraint atau logika aplikasi.

5. Query Bi-temporal: “Time Travel” di Data Anda

Mari kita lihat bagaimana kita bisa melakukan query yang powerful dengan data bi-temporal ini.

✅ A. Mencari Data “Saat Ini”

Mencari harga produk PROD001 yang berlaku hari ini (misal, 2023-01-15) dan merupakan versi terbaru yang diketahui sistem:

SELECT price
FROM ProductPriceHistory
WHERE product_id = 'PROD001'
  AND NOW() BETWEEN valid_from AND valid_to
  AND transaction_to IS NULL;

Jika valid_to bisa NULL untuk “berlaku terus”, query-nya bisa sedikit dimodifikasi:

SELECT price
FROM ProductPriceHistory
WHERE product_id = 'PROD001'
  AND valid_from <= NOW()
  AND (valid_to IS NULL OR valid_to >= NOW())
  AND transaction_to IS NULL;

Ini akan mengembalikan Rp 100.000 karena NOW() (15 Jan) berada dalam valid_time Record 1.

✅ B. Mencari Data pada Valid Time Tertentu (As-of Query)

Mencari harga produk PROD001 yang berlaku pada tanggal 2023-02-10 (tanggal di dunia nyata), menurut informasi terbaru yang diketahui sistem:

SELECT price
FROM ProductPriceHistory
WHERE product_id = 'PROD001'
  AND valid_from <= '2023-02-10 00:00:00'
  AND (valid_to IS NULL OR valid_to >= '2023-02-10 00:00:00')
  AND transaction_to IS NULL;

Ini akan mengembalikan Rp 120.000 karena pada 10 Feb, valid_time Record 2 yang aktif.

✅ C. Mencari Data pada Transaction Time Tertentu (As-was Query)

Mencari harga produk PROD001 yang berlaku pada 2023-01-15 (Valid Time), sebagaimana diketahui oleh sistem pada tanggal 2023-01-01 (Transaction Time):

SELECT price
FROM ProductPriceHistory
WHERE product_id = 'PROD001'
  AND valid_from <= '2023-01-15 00:00:00'
  AND (valid_to IS NULL OR valid_to >= '2023-01-15 00:00:00')
  AND transaction_from <= '2023-01-01 00:00:00'
  AND (transaction_to IS NULL OR transaction_to >= '2023-01-01 00:00:00');

Dengan data contoh di atas, ini akan mengembalikan Rp 100.000 (Record 1). Karena pada 1 Januari 2023, sistem kita hanya tahu tentang Record 1. Record 2 baru dicatat pada 25 Jan 2023.

✅ D. Mencari Data “Time Travel” (Bi-temporal Query)

Mencari harga produk PROD001 yang berlaku pada tanggal 2023-02-10 (Valid Time), sebagaimana diketahui oleh sistem pada tanggal 2023-01-20 (Transaction Time):

SELECT price
FROM ProductPriceHistory
WHERE product_id = 'PROD001'
  AND valid_from <= '2023-02-10 00:00:00'
  AND (valid_to IS NULL OR valid_to >= '2023-02-10 00:00:00')
  AND transaction_from <= '2023-01-20 00:00:00'
  AND (transaction_to IS NULL OR transaction_to >= '2023-01-20 00:00:00');

Dalam kasus ini, tidak ada record yang memenuhi kriteria tersebut dengan data contoh kita. Record 1 tidak berlaku di Feb, dan Record 2 belum dicatat pada 20 Jan. Ini menunjukkan bagaimana query bi-temporal bisa sangat spesifik.

6. Mengelola Perubahan Data Bi-temporal

Mengelola data bi-temporal sedikit lebih kompleks daripada CRUD biasa.

🎯 A. Menambahkan Data Baru (Insert)

Ketika ada informasi harga baru untuk periode validitas baru:

-- Harga Rp 150.000 berlaku mulai 2023-03-01
INSERT INTO ProductPriceHistory (product_id, price, currency, valid_from, valid_to, transaction_from, transaction_to)
VALUES ('PROD001', 150.00, 'IDR', '2023-03-01 00:00:00', NULL, NOW(), NULL);

Ini akan menambah record baru dengan transaction_from saat ini dan transaction_to NULL.

🎯 B. Mengupdate Data (Update)

Sebenarnya tidak ada “update” dalam arti tradisional. Ketika sebuah nilai berubah, Anda membuat record baru dan “mengakhiri” record lama.

Misal, harga Rp 120.000 yang semula berlaku dari 1 Feb 2023 dan seterusnya, ternyata hanya berlaku sampai 15 Feb 2023 saja.

  1. Akhiri Record Lama: Set valid_to dan transaction_to untuk record yang validitasnya berubah.
    UPDATE ProductPriceHistory
    SET valid_to = '2023-02-15 23:59:59',
        transaction_to = NOW()
    WHERE product_id = 'PROD001'
      AND valid_from = '2023-02-01 00:00:00' -- Identifikasi record yang tepat
      AND transaction_to IS NULL;
  2. Masukkan Record Baru: Buat record baru untuk periode validitas selanjutnya.
    INSERT INTO ProductPriceHistory (product_id, price, currency, valid_from, valid_to, transaction_from, transaction_to)
    VALUES ('PROD001', 130.00, 'IDR', '2023-02-16 00:00:00', NULL, NOW(), NULL);

Ini memastikan jejak histori tetap utuh.

🎯 C. Menghapus Data (Delete)

Seperti update, “menghapus” biasanya berarti mengakhiri validitas sebuah record. Misalnya, sebuah promo dibatalkan. Anda tidak menghapus record, tetapi set valid_to dan transaction_to record tersebut.

UPDATE ProductPriceHistory
SET valid_to = '2023-01-10 23:59:59', -- Akhiri validitas lebih awal
    transaction_to = NOW()
WHERE product_id = 'PROD001'
  AND valid_from = '2023-01-01 00:00:00'
  AND transaction_to IS NULL;

Record tersebut masih ada di database, tetapi tidak lagi dianggap valid setelah 10 Januari 2023.

7. Tantangan dan Pertimbangan

Pemodelan bi-temporal memang powerful, tetapi ada beberapa hal yang perlu diperhatikan:

Kesimpulan

Pemodelan data bi-temporal adalah teknik yang sangat berguna untuk aplikasi yang membutuhkan auditabilitas, pelacakan histori, dan kemampuan untuk “kembali ke masa lalu” dalam data. Dengan memahami perbedaan antara Valid Time dan Transaction Time, serta menerapkan pola skema dan query yang tepat, Anda dapat membangun sistem yang lebih tangguh dan informatif.

Meskipun ada peningkatan kompleksitas, manfaat jangka panjang dalam hal akurasi data, kepatuhan, dan kemampuan analisis seringkali jauh melebihi tantangan implementasinya. Jadi, lain kali Anda berhadapan dengan data yang berubah, pertimbangkan untuk memberi data Anda kemampuan “time travel” dengan bi-temporal!

🔗 Baca Juga