0Pricing
SQL Academy · Ders

Olaylardan Durumu Yeniden Oluşturma

Olayları mevcut duruma katlayın.

Olaylardan Durumu Yeniden Oluşturma, CoddyKit'te ücretsiz bir SQL Academy dersidir. Bu, 4 dersinin 4. dersidir. Aşağıdan dersin tamamını ücretsiz okuyabilir, sonra tarayıcıda yerleşik kod editörü ve 7/24 yapay zeka koçu ile uygulamalı olarak pratik yapabilirsin. Bu, SQL Academy öğrenme yolunun bir parçasıdır ve ilerlemeniz web ve CoddyKit uygulaması arasında senkronize olur. SQL Academy kursu toplamda 4 dersten oluşur.

Durumu Yeniden Oluşturmak Ne Anlama Gelir?

Olay kaynaklamada veriler, değiştirilebilir satırlar yerine değişmez bir olay günlüğü olarak saklanır. Herhangi bir şeyin güncel durumunu öğrenmek için bu olayları yeniden oynatıp tek bir sonuçta birleştirmeniz gerekir.

Buna olaylardan durumu yeniden oluşturma denir. Bir banka hesabını düşünün: bakiyeyi saklamak yerine her para yatırma ve para çekme işlemini saklarsınız. Bakiye, tüm bu olayların toplamıdır.

Basit Bir Olay Tablosu

Bir banka hesabı sistemi için en temel olay günlüğünü oluşturarak başlayalım. Her satır, tutarı ve zaman damgasıyla birlikte gerçekleşmiş bir olayı — para yatırma veya para çekme işlemini — temsil eder.

Bu tablo hiçbir zaman güncellenmez veya silinmez. Yeni olgular her zaman yeni satırlar olarak sona eklenir.

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');

Olayları Bakiyede Birleştirme

Güncel bakiyeyi yeniden oluşturmak için tüm olayları toplarız. Yatırmalar bakiyeye eklenir, çekimler ise bakiyeden çıkarılır. Bir CASE ifadesi, toplamı almadan önce her olay türünü doğru işaretle ele almamızı sağlar.

Bu tek sorgu, tamamen geçmiş olay günlüğünden türetilen mevcut durumu verir.

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;

Belirli Bir Zamandaki Durum

Olay kaynaklamanın en güçlü yönlerinden biri, durumu zamandaki herhangi bir noktada yeniden oluşturabilmenizdir. Toplama işleminden önce yalnızca bir WHERE created_at <= :target_time filtresi eklemeniz yeterlidir.

Bu sayede ek bir şema değişikliği gerektirmeden zamanda geriye dönük sorgu çalıştırabilirsiniz; geçmiş zaten olay günlüğünde tutulmaktadır.

-- 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;

Pencere İşlevleriyle Birikimli Bakiye

Tek bir toplam yerine birikimli bakiyeyi (yani her olaydan sonraki bakiyeyi) hesaplayabiliriz. SUM(...) OVER (ORDER BY ...) pencere işlevi, olaylar kronolojik sırayla biriktikçe kümülatif toplamı hesaplar.

Bu yöntem, denetim izleri ve durum geçişlerinde hata ayıklama için son derece kullanışlıdır.

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;

Durumu Anlık Görüntü Tablosunda Somutlaştırma

Günlük büyüdükçe her sorguda tüm olayları yeniden oynatmak pahalı hâle gelebilir. Yaygın bir iyileştirme, güncel durumu bir anlık görüntü tablosunda somutlaştırmak ve bu tabloyu düzenli aralıklarla ya da isteğe bağlı olarak yeniden oluşturmaktır.

Anlık görüntüsü, birleştirilmiş sonucu saklar; sorgular her seferinde günlüğün tamamını yeniden oynatmak yerine anlık görüntüsünden okuma yapar.

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;

Artımlı Anlık Görüntü Güncellemeleri

Yeni olaylar geldiğinde geçmişin tamamını yeniden oynatmanız gerekmez. Anlık görüntüsünde işlenen son event_id değerini kaydettiyseniz yalnızca farkı, yani anlık görüntüsü alındıktan sonra gelen olayları uygulayabilirsiniz.

Bu artımlı yaklaşım, büyük günlüklerde bile anlık görüntüsü yenilemelerini hızlı tutar.

-- 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;

Zamansal Tablolar ve SYSTEM VERSIONING

SQL:2011, veritabanının kendisinin yönettiği sistem sürümlü zamansal tabloları kullanıma sundu. Her satıra, altyapı tarafından yönetilen valid_from ve valid_to sütunları otomatik olarak eklenir.

PostgreSQL bunu yerel olarak desteklemez, ancak bu davranışı taklit edebilirsiniz. MariaDB ve SQL Server gibi diğer veritabanları ise WITH SYSTEM VERSIONING ifadesini doğrudan destekler.

-- 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');

Zamansal Geçmişi Sorgulama

Öykünülmüş zamansal tablo hazır olduğunda, geçerlilik aralığına göre filtreleyerek bakiyenin geçmişteki herhangi bir anda ne olduğunu sorabilirsiniz. Aralığı hedef zaman damgasını içeren satır, o andaki durumu gösterir.

Bu yaklaşım, sorgu mantığını olayların yeniden oynatılmasından ayırır; durum geçmişi tablosu önceden birleştirilmiştir.

-- 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';

Birden Çok Varlıkla Olay Kaynaklama

Gerçek sistemler aynı anda birçok varlıkla ilgili olayları izler. entity_id ve entity_type sütunlarına sahip ortak bir olay günlüğü, tek bir tablodan herhangi bir nesnenin durumunu yeniden oluşturmanıza olanak tanır.

Burada birden çok ürünün stok hareketlerini izliyoruz. Her ürünün güncel stokunu yeniden oluşturmak yine yalnızca gruplandırılmış bir toplam alma işlemidir.

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;

Açıklık İçin CTE Kullanımı

Durumu yeniden oluşturan sorgular karmaşıklaşabilir. Birleştirme adımını bir CTE içine almak okunabilirliği artırır ve yeniden oluşturulan durumu diğer tablolarla temiz bir şekilde birleştirmenizi sağlar.

Burada hesap bakiyelerini yeniden oluşturuyor, ardından çıktıdaki sahip adlarını da göstermek için bunları bir hesaplar başvuru tablosuyla birleştiriyoruz.

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;

Bilgi Kontrolü

SQL'de olaylardan durumu yeniden oluşturma konusunu anlayıp anlamadığınızı sınayın.

Ders Özeti

Bu derste, SQL kullanarak değiştirilemez bir olay günlüğünden güncel ve geçmiş durumları nasıl yeniden oluşturacağınızı öğrendiniz.

Önemli noktalar:

  • Durum, SUM içinde yer alan işaretli bir CASE ifadesiyle olayların birleştirilmesi (toplanması) sonucunda elde edilir.
  • Zaman damgası filtresi ekleyerek hiçbir ek ücret ödemeden belirli bir zamandaki sorguları çalıştırabilirsiniz.
  • Pencere işlevleri, her olaydan sonraki birikimli durumu üretir.
  • Anlık görüntüsü tabloları, performans için birleştirilmiş sonucu somutlaştırır; artımlı güncellemeler yalnızca yeni olayları uygular.
  • Öykünülmüş zamansal tablolar, geçmişe hızlı erişim için geçerlilik aralıklarına sahip, önceden birleştirilmiş durum satırlarını saklar.
  • CTE'ler, türetilen durumu diğer tablolarla birleştirmeniz gerektiğinde yeniden oluşturma sorgularının okunabilir kalmasını sağlar.

Bu yaklaşımlar, olay kaynaklı ve denetime uygun veritabanı tasarımlarının temelini oluşturur.

Sıkça Sorulan Sorular

“Olaylardan Durumu Yeniden Oluşturma” dersi ücretsiz mi?

Evet — “Olaylardan Durumu Yeniden Oluşturma” dersin tüm metni burada web'de ücretsiz olarak okunabilir. Etkileşimli olarak pratik yapmak (yerleşik kod editörü ve 7/24 yapay zeka koçu) ve SQL Academy kursunun geri kalanını açmak için CoddyKit PRO'ya yükselt. SQL Academy kursu toplamda 4 dersten oluşur.

“Olaylardan Durumu Yeniden Oluşturma” dersinde ne öğreneceğim?

Olayları mevcut duruma katlayın. SQL Academy ile uygulamalı kodu tarayıcıda doğrudan çalıştırarak pratik yaparsın ve 7/24 yapay zeka koçu dersi çalışırken sorularını yanıtlar.

SQL Academy öğrenmeye başlamak için deneyim gerekli mi?

Önceden deneyim gerekmez. CoddyKit'te SQL Academy, başlangıçtan ileri seviyeye kadar yapılandırıldığı için buradan başlayabilir veya başından başlayıp kendi hızında ilerleme yapabilirsin. Bu, 4 dersinin 4. dersidir.

“Olaylardan Durumu Yeniden Oluşturma” dersi ne kadar sürer?

Çoğu CoddyKit dersi yaklaşık 5–10 dakika sürer. Her biri kısa ve etkileşimli olduğu için sabit ilerleme yaparsın ve web ile uygulama arasında tam olarak bıraktığın yerden devam edebilirsin.

Bu SQL Academy dersinde kod yazıp çalıştırabilir miyim?

Evet. Her SQL Academy dersi yerleşik bir kod editörü içerir, bu sayede tarayıcıda gerçek kod yazıp çalıştırabilir ve anlık yapay zeka geri bildirimi alırsın — yerel kurulum gerekli değildir.

Bu kursun tüm dersleri

  1. Geçmişi Neden Saklamalı?
  2. Yalnızca Ekleme Yapılan Olay Tabloları
  3. Zamansal ve Sürümlenmiş Satırlar
  4. Olaylardan Durumu Yeniden Oluşturma
← SQL Academy Sayfasına Dön