0Pricing
SQL Academy · Pelajaran

Membangun Ulang Status dari Peristiwa

Gabungkan peristiwa menjadi status saat ini.

Membangun Ulang Status dari Peristiwa adalah pelajaran SQL Academy gratis di CoddyKit. Ini adalah pelajaran 4 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar SQL Academy, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus SQL Academy mencakup 4 pelajaran total.

Apa Arti Membangun Ulang Keadaan?

Dalam sumber peristiwa, data disimpan sebagai catatan peristiwa yang tidak dapat diubah, bukan sebagai baris yang dapat diubah. Untuk mengetahui keadaan saat ini dari sesuatu, Anda harus memutar ulang peristiwa tersebut dan mengolahnya hingga menjadi satu hasil.

Proses ini disebut membangun ulang keadaan dari peristiwa. Bayangkan sebuah rekening bank: alih-alih menyimpan saldonya, Anda menyimpan setiap setoran dan penarikan. Saldo selalu merupakan jumlah dari semua peristiwa tersebut.

Tabel Peristiwa Sederhana

Mari mulai dengan membuat catatan peristiwa minimal untuk sistem rekening bank. Setiap baris mewakili sesuatu yang terjadi—setoran atau penarikan—beserta jumlah dan stempel waktunya.

Tabel ini tidak pernah diperbarui atau dihapus. Fakta baru selalu ditambahkan sebagai baris baru.

CREATE TABLE account_events (
  event_id   SERIAL PRIMARY KEY,
  account_id INT NOT NULL,
  event_type VARCHAR(20) NOT NULL,  -- 'deposit' or 'withdrawal'
  amount     NUMERIC(12, 2) NOT NULL,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

INSERT INTO account_events (account_id, event_type, amount, created_at) VALUES
  (1, 'deposit',    1000.00, '2024-01-01 09:00:00+00'),
  (1, 'deposit',     500.00, '2024-01-03 14:00:00+00'),
  (1, 'withdrawal',  200.00, '2024-01-05 10:00:00+00'),
  (1, 'deposit',     300.00, '2024-01-07 11:00:00+00'),
  (1, 'withdrawal',  150.00, '2024-01-09 16:00:00+00');

Menggabungkan Peristiwa Menjadi Saldo

Untuk membangun ulang saldo saat ini, kita menggabungkan semua peristiwa. Setoran menambah saldo, sedangkan penarikan menguranginya. Ekspresi CASE memungkinkan kita memperlakukan setiap jenis peristiwa dengan tanda yang tepat sebelum menjumlahkannya.

Satu kueri ini memberikan keadaan saat ini yang sepenuhnya diturunkan dari catatan peristiwa historis.

SELECT
  account_id,
  SUM(
    CASE event_type
      WHEN 'deposit'    THEN  amount
      WHEN 'withdrawal' THEN -amount
      ELSE 0
    END
  ) AS current_balance
FROM account_events
WHERE account_id = 1
GROUP BY account_id;

Keadaan pada Titik Waktu Tertentu

Salah satu aspek paling kuat dari pencatatan berbasis peristiwa adalah kemampuan untuk membangun ulang keadaan pada titik waktu mana pun. Cukup tambahkan penyaring WHERE created_at <= :target_time sebelum melakukan agregasi.

Dengan begitu, Anda mendapatkan kueri lintas waktu tanpa memerlukan perubahan skema tambahan — riwayatnya sudah tersedia di dalam catatan peristiwa.

-- What was the balance at the end of January 5th?
SELECT
  account_id,
  SUM(
    CASE event_type
      WHEN 'deposit'    THEN  amount
      WHEN 'withdrawal' THEN -amount
      ELSE 0
    END
  ) AS balance_at_snapshot
FROM account_events
WHERE account_id = 1
  AND created_at <= '2024-01-05 23:59:59+00'
GROUP BY account_id;

Saldo Berjalan dengan Fungsi Jendela

Alih-alih hanya menghitung satu total, kita dapat menghitung saldo berjalan — saldo setelah setiap peristiwa. Fungsi jendela SUM(...) OVER (ORDER BY ...) menghitung jumlah kumulatif saat peristiwa terakumulasi dalam urutan kronologis.

Hal ini sangat berguna untuk jejak audit dan penelusuran kesalahan pada transisi keadaan.

SELECT
  event_id,
  created_at,
  event_type,
  amount,
  SUM(
    CASE event_type
      WHEN 'deposit'    THEN  amount
      WHEN 'withdrawal' THEN -amount
      ELSE 0
    END
  ) OVER (PARTITION BY account_id ORDER BY created_at, event_id)
    AS running_balance
FROM account_events
WHERE account_id = 1
ORDER BY created_at, event_id;

Menyimpan Keadaan dalam Tabel Cuplikan

Memutar ulang semua peristiwa pada setiap kueri dapat menjadi mahal seiring catatan bertambah panjang. Salah satu pengoptimalan yang umum adalah menyimpan keadaan saat ini secara nyata dalam tabel cuplikan, lalu membangunnya ulang secara berkala atau sesuai permintaan.

Cuplikan menyimpan hasil penggabungan; kueri membaca dari cuplikan, bukan memutar ulang seluruh catatan setiap kali.

CREATE TABLE account_snapshots (
  account_id      INT PRIMARY KEY,
  current_balance NUMERIC(12, 2) NOT NULL,
  as_of_event_id  INT NOT NULL,
  updated_at      TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- Populate / refresh the snapshot from the event log
INSERT INTO account_snapshots (account_id, current_balance, as_of_event_id, updated_at)
SELECT
  account_id,
  SUM(CASE event_type WHEN 'deposit' THEN amount WHEN 'withdrawal' THEN -amount ELSE 0 END),
  MAX(event_id),
  NOW()
FROM account_events
GROUP BY account_id
ON CONFLICT (account_id) DO UPDATE
  SET current_balance = EXCLUDED.current_balance,
      as_of_event_id  = EXCLUDED.as_of_event_id,
      updated_at      = EXCLUDED.updated_at;

Pembaruan Cuplikan Bertahap

Saat peristiwa baru tiba, Anda tidak perlu memutar ulang seluruh riwayat. Jika Anda mencatat event_id terakhir yang diproses di dalam cuplikan, Anda dapat menerapkan hanya selisihnya — peristiwa yang tiba setelah cuplikan dibuat.

Pola bertahap ini membuat penyegaran cuplikan tetap cepat, bahkan pada catatan berukuran besar.

-- Apply only new events since the last snapshot
UPDATE account_snapshots AS snap
SET
  current_balance = snap.current_balance + delta.net,
  as_of_event_id  = delta.max_event_id,
  updated_at      = NOW()
FROM (
  SELECT
    ae.account_id,
    SUM(CASE ae.event_type WHEN 'deposit' THEN ae.amount WHEN 'withdrawal' THEN -ae.amount ELSE 0 END) AS net,
    MAX(ae.event_id) AS max_event_id
  FROM account_events ae
  JOIN account_snapshots s ON s.account_id = ae.account_id
  WHERE ae.event_id > s.as_of_event_id
  GROUP BY ae.account_id
) AS delta
WHERE snap.account_id = delta.account_id;

Tabel Berwaktu dan Pemberian Versi oleh Sistem

SQL:2011 memperkenalkan tabel berwaktu berversi sistem, yang dikelola oleh basis data itu sendiri. Setiap baris secara otomatis mendapatkan kolom valid_from dan valid_to yang dikelola oleh mesin basis data.

PostgreSQL tidak mendukungnya secara bawaan, tetapi Anda dapat menirunya. Basis data lain seperti MariaDB dan SQL Server mendukung WITH SYSTEM VERSIONING secara langsung.

-- Emulating a temporal table in PostgreSQL
CREATE TABLE account_state_history (
  account_id      INT NOT NULL,
  current_balance NUMERIC(12, 2) NOT NULL,
  valid_from      TIMESTAMPTZ NOT NULL,
  valid_to        TIMESTAMPTZ NOT NULL DEFAULT 'infinity'
);

-- Insert initial state
INSERT INTO account_state_history (account_id, current_balance, valid_from)
VALUES (1, 1000.00, '2024-01-01 09:00:00+00');

-- On update: close old row, insert new row
UPDATE account_state_history
  SET valid_to = '2024-01-03 14:00:00+00'
WHERE account_id = 1 AND valid_to = 'infinity';

INSERT INTO account_state_history (account_id, current_balance, valid_from)
VALUES (1, 1500.00, '2024-01-03 14:00:00+00');

Membuat Kueri atas Riwayat Berwaktu

Dengan tabel berwaktu yang ditiru, Anda dapat menanyakan berapa saldo pada waktu lampau mana pun dengan menyaring berdasarkan rentang validitas. Baris yang rentangnya mencakup cap waktu target merupakan keadaan pada waktu tersebut.

Pola ini memisahkan logika kueri dari pemutaran ulang peristiwa — tabel riwayat keadaan sudah digabungkan sebelumnya.

-- What was the account balance on January 4th?
SELECT
  account_id,
  current_balance,
  valid_from,
  valid_to
FROM account_state_history
WHERE account_id = 1
  AND valid_from <= '2024-01-04 00:00:00+00'
  AND valid_to   >  '2024-01-04 00:00:00+00';

Pencatatan Berbasis Peristiwa dengan Banyak Entitas

Sistem nyata melacak peristiwa untuk banyak entitas sekaligus. Catatan peristiwa bersama dengan kolom entity_id dan entity_type memungkinkan Anda membangun ulang keadaan objek apa pun dari satu tabel.

Di sini kita melacak perpindahan persediaan di berbagai produk. Membangun ulang stok saat ini untuk setiap produk sekali lagi hanyalah agregasi berkelompok.

CREATE TABLE inventory_events (
  event_id    SERIAL PRIMARY KEY,
  product_id  INT NOT NULL,
  event_type  VARCHAR(20) NOT NULL,  -- 'received', 'shipped', 'adjusted'
  quantity    INT NOT NULL,
  created_at  TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

INSERT INTO inventory_events (product_id, event_type, quantity, created_at) VALUES
  (101, 'received',  200, '2024-03-01 08:00:00+00'),
  (101, 'shipped',    50, '2024-03-02 12:00:00+00'),
  (101, 'shipped',    30, '2024-03-04 15:00:00+00'),
  (102, 'received',  150, '2024-03-01 08:00:00+00'),
  (102, 'adjusted',  -10, '2024-03-03 09:00:00+00');

-- Rebuild current stock for all products
SELECT
  product_id,
  SUM(CASE event_type WHEN 'received' THEN quantity WHEN 'shipped' THEN -quantity ELSE quantity END) AS stock_on_hand
FROM inventory_events
GROUP BY product_id
ORDER BY product_id;

Menggunakan CTE untuk Kejelasan

Kueri untuk membangun ulang keadaan dapat menjadi rumit. Membungkus langkah penggabungan dalam CTE meningkatkan keterbacaan dan memungkinkan Anda menggabungkan keadaan hasil pembangunan ulang dengan tabel lain secara rapi.

Di sini kita membangun ulang saldo akun, lalu menggabungkannya dengan tabel referensi akun untuk menyertakan nama pemilik dalam keluaran.

CREATE TABLE accounts (
  account_id INT PRIMARY KEY,
  owner_name VARCHAR(100) NOT NULL
);

INSERT INTO accounts (account_id, owner_name) VALUES
  (1, 'Alice'),
  (2, 'Bob');

INSERT INTO account_events (account_id, event_type, amount, created_at) VALUES
  (2, 'deposit',   2000.00, '2024-01-02 10:00:00+00'),
  (2, 'withdrawal', 400.00, '2024-01-06 11:00:00+00');

WITH rebuilt_balances AS (
  SELECT
    account_id,
    SUM(CASE event_type WHEN 'deposit' THEN amount WHEN 'withdrawal' THEN -amount ELSE 0 END) AS balance
  FROM account_events
  GROUP BY account_id
)
SELECT
  a.account_id,
  a.owner_name,
  rb.balance
FROM accounts a
JOIN rebuilt_balances rb USING (account_id)
ORDER BY a.account_id;

Uji Pemahaman

Uji pemahaman Anda tentang cara membangun ulang keadaan dari peristiwa dalam SQL.

Ringkasan Pelajaran

Dalam pelajaran ini, Anda mempelajari cara membangun ulang keadaan saat ini dan historis dari catatan peristiwa yang tidak dapat diubah menggunakan SQL.

Hal penting:

  • Keadaan diturunkan dengan menggabungkan (mengagregasikan) peristiwa menggunakan ekspresi CASE bertanda di dalam SUM.
  • Menambahkan penyaring cap waktu memberi Anda kueri pada titik waktu tertentu secara cuma-cuma.
  • Fungsi jendela menghasilkan keadaan berjalan setelah setiap peristiwa.
  • Tabel cuplikan menyimpan hasil penggabungan secara nyata untuk meningkatkan kinerja; pembaruan bertahap hanya menerapkan peristiwa baru.
  • Tabel berwaktu yang ditiru menyimpan baris keadaan yang sudah digabungkan dengan rentang validitas untuk pencarian riwayat yang cepat.
  • CTE menjaga kueri pembangunan ulang tetap mudah dibaca saat Anda perlu menggabungkan keadaan turunan dengan tabel lain.

Pola-pola ini menjadi dasar rancangan basis data berbasis peristiwa dan yang mendukung audit.

Pertanyaan yang Sering Diajukan

Apakah pelajaran “Membangun Ulang Status dari Peristiwa” gratis?

Ya — teks lengkap “Membangun Ulang Status dari Peristiwa” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus SQL Academy, upgrade ke CoddyKit PRO. Kursus SQL Academy mencakup 4 pelajaran total.

Apa yang akan aku pelajari di “Membangun Ulang Status dari Peristiwa”?

Gabungkan peristiwa menjadi status saat ini. Kamu berlatih SQL Academy dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.

Apakah aku perlu pengalaman untuk memulai SQL Academy?

Tidak diperlukan pengalaman sebelumnya. SQL Academy di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 4 dari 4.

Berapa lama pelajaran “Membangun Ulang Status dari Peristiwa” memakan waktu?

Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.

Bisakah aku menulis dan menjalankan kode dalam pelajaran SQL Academy ini?

Ya. Setiap pelajaran SQL Academy menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.

Semua pelajaran dalam kursus ini

  1. Mengapa Menyimpan Riwayat
  2. Tabel Peristiwa Hanya-Tambah
  3. Baris Temporal dan Berversi
  4. Membangun Ulang Status dari Peristiwa
← Kembali ke SQL Academy