- Untuk menjawab pertanyaan seperti seperti apa data pada tanggal tertentu (Selasa lalu), Postgres 19 memperkenalkan dukungan tabel temporal native (temporal table), sehingga data sebelum dan sesudah perubahan dapat dilacak tanpa sistem trigger audit terpisah
- Standar SQL:2011 mendefinisikan tabel temporal lebih dari 10 tahun lalu, dan Postgres akhirnya memasukkannya ke core, bergabung lebih lambat dibanding mesin DB lain
- Alih-alih dua kolom
valid_from/valid_todan exclusion constraint berbasis ekstensibtree_gist, Postgres menyediakan ekspresi yang lebih intuitif lewat satu kolom range type dan constraintWITHOUT OVERLAPS - Dengan sintaks
FOR PORTION OF, pemecahan baris otomatis saat UPDATE·DELETE ditangani oleh engine, sekaligus mencegah celah dan tumpang tindih pada sumbu waktu - Pengenalan ini mencakup separuh dari sistem bi-temporal, yaitu application time, belum mendukung system time, tetapi menyiapkan fondasi untuk rilis mendatang
Cara Lama - The Old-Fashioned Way
- Upaya pertama untuk melacak harga produk secara kronologis terdiri dari dua kolom tanggal
valid_from,valid_todan CHECK constraintvalid_from < valid_to- Namun ini tidak mencegah penyisipan dua baris dengan rentang tanggal yang tumpang tindih untuk produk yang sama (misalnya produk nomor 42 berharga $9.99 sekaligus $14.99 pada hari Selasa yang sama)
- Solusi tradisional adalah menggunakan ekstensi
btree_gistdan exclusion constraint- Dalam bentuk
EXCLUDE USING gist (product_id WITH =, daterange(valid_from, valid_to) WITH &&), akan muncul error saat memasukkan baris yang tumpang tindih
- Dalam bentuk
- Masalah dari pendekatan ini
- GiST adalah indeks khas Postgres yang membutuhkan pengalaman, dan karena berupa ekstensi opsional, ada hambatan masuk
- Sintaks exclusion constraint tidak intuitif, sehingga sulit terpikir sebagai pendekatan standar
- Tabel itu sendiri tidak memiliki kesadaran temporal bawaan; saat rentang waktu berubah, baris harus dipecah atau digabung secara manual, dan beban konsistensi temporal dialihkan ke aplikasi
Sejarah Singkat Waktu - A Brief History of Time
- Standar SQL:2011 memperkenalkan periode waktu valid (
APPLICATION TIME), constraintWITHOUT OVERLAPS, dan sintaksFOR PORTION OFuntuk manipulasi data temporal - Henrietta Dombrovskaya (Hetti) bersama Chad Slaughter mengembangkan ekstensi pg_bitemporal, sebuah framework untuk mengelola tabel bi-temporal di dalam Postgres dengan PL/pgSQL
- Sejak 2015, mereka mempresentasikan konsep ini di berbagai konferensi, mendemonstrasikan cara melacak valid time (waktu ketika fakta benar di dunia nyata) dan transaction time (waktu ketika DB mencatat fakta) secara bersamaan
- Perbedaan dua dimensi waktu
- Valid time berarti "harga ini berlaku dari Januari hingga Juni"
- Transaction time adalah sudut pandang DB: "baris ini disisipkan pada 12 Maret pukul 15:47, lalu diganti pada 3 April pukul 09:01"
- Dengan menggabungkan keduanya, tabel bi-temporal dapat menjawab "berdasarkan informasi yang diketahui saat itu, berapa harga yang kita kira berlaku pada Selasa lalu"
- pg_bitemporal menggunakan
EXCLUDE USING gistdua kali, masing-masing untuk rentangeffective(valid time) dan rentangasserted(transaction time)- Menyediakan fungsi untuk penyisipan, pembaruan, koreksi, penonaktifan, dan penghapusan bi-temporal, serta implementasi relasi interval Allen (Allen's interval relationships) untuk penalaran temporal
- Batasan ekstensi
- Tidak dapat mengubah query planner agar mengenali kondisi temporal, berintegrasi dengan sistem constraint level engine, atau menyediakan sintaks manipulasi native → perlu masuk ke core
- Postgres 19 menerima separuh valid time dari sistem bi-temporal; belum lengkap, tetapi merupakan kemajuan besar
Rentang sebagai Penyelamat - Ranges to the Rescue
- Pendekatan Postgres 19 menggunakan satu kolom range type
valid_at DATERANGE, bukanvalid_from/valid_toterpisah- Dengan
PRIMARY KEY (product_id, valid_at WITHOUT OVERLAPS), ekstensibtree_gistdan exclusion constraint tidak lagi diperlukan WITHOUT OVERLAPSmemastikanproduct_idunik pada setiap titik waktu, tetapi tetap mengizinkan beberapa baris untuk produk yang sama selama rentangnya tidak tumpang tindih
- Dengan
- Secara internal masih menggunakan indeks GiST dan memerlukan
btree_gistuntuk kolom non-temporal pada key, tetapi Postgres otomatis menangani dependensi saat inisialisasi constraint - Notasi rentang:
[berarti inklusif,)berarti eksklusif →[2025-01-01, 2025-07-01)mencakup 1 Januari dan tidak mencakup 1 Juli- Baris Gadget terakhir
[2026-01-01,)adalah rentang open-ended tanpa tanggal akhir yang ditentukan untuk harga saat ini
- Baris Gadget terakhir
- Perilaku perlindungan terhadap tumpang tindih
- Saat menyisipkan rentang salah
[2025-03-01, 2025-01-01), muncul error bahwa "batas bawah rentang harus kurang dari atau sama dengan batas atas" - Saat menyisipkan rentang tumpang tindih
[2025-03-01, 2025-09-01), muncul error pelanggaran exclusion constraintproducts_pkey - Dengan menggunakan range saja, dua jenis validasi didapat sekaligus
- Saat menyisipkan rentang salah
Mengiris dan Membagi - Slicing and Dicing
- Jika harga sebuah produk diubah menjadi $10.99 hanya untuk Maret–September 2025, cara lama mengharuskan baris dipecah dan disisipkan secara manual, sehingga kesalahan bisa menimbulkan celah atau tumpang tindih
- Pada tabel temporal, maksud tersebut dapat diekspresikan secara langsung
UPDATE products FOR PORTION OF valid_at FROM '2025-03-01' TO '2025-09-01' SET price = 10.99 WHERE product_id = 1
- Hasilnya, baris Widget bertambah dari 3 menjadi 5
- Baris $9.99 yang lama dipersempit menjadi
[2025-01-01, 2025-03-01) - Baris baru $10.99 ditambahkan untuk rentang yang tersisa
- Baris $12.99 yang lama dipersempit menjadi
[2025-09-01, 2026-01-01) - Baris baru $10.99 ditambahkan untuk rentang tersisa
[2025-07-01, 2025-09-01)
- Baris $9.99 yang lama dipersempit menjadi
- Alasan $10.99 terbagi menjadi dua baris adalah karena
FOR PORTION OFmemproses setiap baris yang cocok secara independen, dan tidak melakukan penggabungan (coalesce) rentang yang bersebelahan setelahnya- Hasil akhirnya tidak memiliki celah atau tumpang tindih, sebuah keuntungan yang tidak ada pada exclusion logic murni
- Kasus tepi (edge case)
- Jika rentang
FOR PORTION OFsepenuhnya berada di dalam satu baris yang ada, hingga 2 baris sisa (depan dan belakang) dibuat - Jika tepat cocok dengan batas yang sudah ada, baris sisa tidak diperlukan
- Jika rentang
- Baris sisa temporal yang baru dibuat tidak memerlukan hak
INSERT, tetapi triggerINSERTyang sudah ada tetap dijalankan → perlu diperhatikan pada audit logging atau fungsi triggerSECURITY DEFINER
Menghapus Riwayat - Erasing History
FOR PORTION OFjuga bekerja padaDELETE; misalnya menghapus sementara produk tertentu dari katalog pada Juni–Oktober 2025DELETE FROM products FOR PORTION OF valid_at FROM '2025-06-01' TO '2025-10-01' WHERE product_id = 2
- Hasilnya
- Periode Juni–Oktober dipotong, dan baris $22.99 yang mencakup
[2025-04-01, 2026-01-01)dipecah menjadi dua baris sisa: satu berakhir pada Juni dan satu mulai pada Oktober - Data harga sebelum dan sesudah celah dipertahankan dengan nilai aslinya, sehingga DELETE justru menambah jumlah baris
- Periode Juni–Oktober dipotong, dan baris $22.99 yang mencakup
- Mekanisme pengelolaan tabel temporal menangani semuanya secara otomatis, menghilangkan risiko penghapusan berlebihan atau orphaned fragment di level aplikasi
Kebenaran dalam Iklan - Truth in Advertising
- Tabel temporal belum lengkap tanpa temporal foreign key, dan Postgres 19 mendukungnya dengan kata kunci
PERIODFOREIGN KEY (product_id, PERIOD valid_at) REFERENCES products (product_id, PERIOD valid_at)
- Kata kunci
PERIODmemberi tahu bahwa foreign key itu sendiri berbasis temporal- Product yang dirujuk harus ada selama seluruh periode rentang
valid_atmilik variant - Kombinasi semua baris yang cocok pada tabel yang dirujuk harus sepenuhnya mencakup periode baris yang mereferensikan
- Product yang dirujuk harus ada selama seluruh periode rentang
- Percobaan membuat variant yang melampaui rentang waktu product (
[2025-01-01, 2027-01-01)) akan ditolak- Karena harga Widget hanya didefinisikan hingga pertengahan 2026, variant yang mengklaim berlaku sampai 2027 ditolak karena pelanggaran foreign key constraint
- Satu batasan penting
- Temporal foreign key hanya mendukung
NO ACTIONsebagai referential action;CASCADE,SET NULL, danSET DEFAULTtidak termasuk - Menghapus baris product yang menjadi dependensi variant akan selalu menghasilkan error, karena kompleksitas operasi temporal berantai, sehingga aplikasi harus menanganinya secara eksplisit
- Temporal foreign key hanya mendukung
Langkah-Langkah Kecil - Baby Steps
- Fitur yang sudah tersedia saat ini: tabel temporal berbasis valid time dengan pencegahan tumpang tindih, manipulasi data temporal, dan temporal foreign key
- Kekurangan terbesar adalah system time, yang juga disebut transaction time
- Valid time melacak kapan sebuah fakta benar di dunia nyata; system time melacak kapan DB mengetahui fakta tersebut, dan banyak sistem memanfaatkan keduanya
- Area inilah yang diisi oleh ekstensi pg_bitemporal sejak 2015
- Emulasi system time dengan trigger memang memungkinkan, tetapi berbeda dari pengelolaan transparan oleh engine seperti fitur temporal baru lainnya
- Dokumentasi tabel temporal juga menyatakan bahwa system time belum didukung secara native dan dapat diemulasi; belum jelas apakah akan hadir di Postgres 20 atau setelahnya, tetapi fondasinya sudah tersedia
Penutup - Final Thoughts
- Pendekatan
EXCLUDE USING gistmemang bekerja, tetapi merupakan workaround yang relatif kasar; ekstensi seperti pg_bitemporal telah membuktikan konsep dan menjaga diskusi tetap berjalan - Pendekatan ini jauh lebih intuitif dibanding exclusion constraint GiST
WITHOUT OVERLAPSpada primary key terbaca seperti bahasa Inggris biasa, danFOR PORTION OFmenggambarkan aksinya secara langsung- Pemecahan baris otomatis saat pembaruan dan penghapusan temporal menghilangkan satu kategori potensi bug
- Perjalanan dari SQL:2011 ke Postgres 19 sangat panjang; Hetti dan komunitas selama bertahun-tahun membuktikan kebutuhan serta kelayakan pola ini, dan kini masuk ke core
- Dukungan system time pada rilis mendatang perlu diperhatikan; jika Postgres memiliki kedua separuh bi-temporal, kemungkinannya akan berkembang jauh lebih luas
Belum ada komentar.