Tabele zdarzeń tylko do dopisywania
Rejestruj, co się wydarzyło, i nigdy nie nadpisuj danych
Tabele zdarzeń tylko do dopisywania to bezpłatna lekcja SQL Academy na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej SQL Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs SQL Academy zawiera 4 lekcji w sumie.
Czym jest tabela append-only?
Tabela zdarzeń append-only to tabela, do której wiersze są wyłącznie wstawiane — nigdy aktualizowane ani usuwane. Każdy wiersz reprezentuje coś, co wydarzyło się w określonym momencie.
Ten wzorzec stanowi podstawę event sourcingu. Zamiast przechowywać bieżący stan, przechowuje się każdą zmianę jako niezmienne zdarzenie, uzyskując kompletną historię, którą można poddać audytowi.
Tworzenie tabeli zdarzeń
Dobrze zaprojektowana tabela zdarzeń rejestruje, kto wykonał jaką czynność, na którym zasobie i kiedy. Kolumna occurred_at przechowuje dokładny znacznik czasu, a DEFAULT NOW() zapewnia jej automatyczne uzupełnienie.
Proszę zauważyć, że w tym projekcie nie ma operacji UPDATE ani DELETE — po zapisaniu wiersze pozostają trwałe.
CREATE TABLE account_events (
id BIGSERIAL PRIMARY KEY,
account_id BIGINT NOT NULL,
event_type TEXT NOT NULL,
payload JSONB,
occurred_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);Wstawianie zdarzeń
Każde działanie użytkownika — zalogowanie, wpłata pieniędzy czy zmiana adresu e-mail — staje się nowym wierszem. Nigdy nie należy wracać do poprzedniego zdarzenia i go edytować. Jeśli trzeba coś skorygować, należy zamiast tego wstawić zdarzenie kompensujące.
Dzięki temu zachowana zostaje pełna sekwencja zdarzeń w kolejności, w jakiej nastąpiły.
INSERT INTO account_events (account_id, event_type, payload)
VALUES
(42, 'account_opened', '{"plan": "free"}'),
(42, 'email_verified', '{"email": "alice@example.com"}'),
(42, 'plan_upgraded', '{"from": "free", "to": "pro"}');Odczytywanie pełnej historii
Ponieważ każda zmiana stanu jest przechowywana jako wiersz, odczytanie pełnej historii konta sprowadza się do prostego użycia SELECT z uporządkowaniem według czasu. Można odtworzyć całe życie rekordu — od pierwszego zdarzenia aż po najnowsze.
SELECT
id,
event_type,
payload,
occurred_at
FROM account_events
WHERE account_id = 42
ORDER BY occurred_at ASC;Wyprowadzanie bieżącego stanu
W tabeli append-only bieżący stan nie jest przechowywany bezpośrednio — jest wyprowadzany przez odczytanie ostatniego odpowiedniego zdarzenia. W tym przykładzie bieżący plan konta 42 wynika z najnowszego zdarzenia plan_upgraded lub account_opened.
Użycie ORDER BY occurred_at DESC LIMIT 1 pozwala wydajnie pobrać najnowszy stan.
SELECT payload->>'to' AS current_plan
FROM account_events
WHERE account_id = 42
AND event_type IN ('account_opened', 'plan_upgraded')
ORDER BY occurred_at DESC
LIMIT 1;Wymuszanie niezmienności za pomocą reguł
Gwarancję działania tabeli append-only można wymusić na poziomie bazy danych za pomocą obiektu RULE, który po cichu ignoruje każdą operację UPDATE lub DELETE na tej tabeli. Zapobiega to przypadkowym modyfikacjom wykonywanym przez dowolną aplikację mającą dostęp do zapisu.
Jeszcze silniejszą alternatywą jest wyzwalacz zgłaszający wyjątek, który aktywnie odrzuca operację i zwraca błąd.
CREATE RULE no_update_events AS
ON UPDATE TO account_events
DO INSTEAD NOTHING;
CREATE RULE no_delete_events AS
ON DELETE TO account_events
DO INSTEAD NOTHING;Niezmienność za pomocą wyzwalacza
Wyzwalacz zgłaszający wyjątek jest bardziej restrykcyjny niż cicha reguła — aplikacja natychmiast otrzymuje błąd, jeśli spróbuje zmodyfikować zdarzenie z przeszłości. Dzięki temu błędy stają się widoczne, zamiast być po cichu ignorowane.
CREATE OR REPLACE FUNCTION deny_event_mutation()
RETURNS TRIGGER AS $$
BEGIN
RAISE EXCEPTION 'Event table is append-only: % is not allowed', TG_OP;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER trg_deny_event_mutation
BEFORE UPDATE OR DELETE ON account_events
FOR EACH ROW EXECUTE FUNCTION deny_event_mutation();Zliczanie zdarzeń w czasie
Tabele append-only ułatwiają analizę danych w czasie. Ponieważ każde zdarzenie ma znacznik czasu, można grupować je według dnia, tygodnia lub miesiąca bez dodawania dodatkowych kolumn. Poniższy przykład zlicza, ile zdarzeń każdego typu wystąpiło danego dnia.
SELECT
DATE_TRUNC('day', occurred_at) AS day,
event_type,
COUNT(*) AS total
FROM account_events
GROUP BY 1, 2
ORDER BY 1, 2;Odtwarzanie stanu na określony moment
Jedną z najpotężniejszych właściwości dziennika zdarzeń jest możliwość odtworzenia stanu dowolnego rekordu takiego, jaki był w przeszłości. Wystarczy odfiltrować zdarzenia do wybranego znacznika czasu — nie jest wymagane żadne rozszerzenie obsługujące podróż w czasie.
Jest to niezwykle przydatne podczas debugowania, audytów i zapewniania zgodności z przepisami.
-- What plan was account 42 on at the end of last month?
SELECT payload->>'to' AS plan_at_snapshot
FROM account_events
WHERE account_id = 42
AND event_type IN ('account_opened', 'plan_upgraded')
AND occurred_at <= DATE_TRUNC('month', NOW()) - INTERVAL '1 second'
ORDER BY occurred_at DESC
LIMIT 1;Zdarzenia kompensujące zamiast poprawek
Po wykryciu błędu — na przykład nieprawidłowego obciążenia — nie należy usuwać błędnego zdarzenia. Zamiast tego należy wstawić zdarzenie kompensujące, które je anuluje lub odwraca. Oba zdarzenia pozostają widoczne w dzienniku, dokładnie pokazując, co się wydarzyło i kiedy wprowadzono korektę.
Dzięki temu ścieżka audytu pozostaje kompletna i widoczna jako niezmieniona.
-- A charge was applied by mistake; record a reversal
INSERT INTO account_events (account_id, event_type, payload)
VALUES (
42,
'charge_reversed',
'{"reason": "billing_error", "reverses_event_id": 17}'
);Partycjonowanie dużych tabel zdarzeń
Tabele zdarzeń szybko rosną. Partycjonowanie według zakresu czasu pozwala utrzymać niewielki rozmiar poszczególnych partycji, przyspiesza zapytania zakresowe oraz umożliwia archiwizowanie lub usuwanie starych partycji bez modyfikowania najnowszych danych.
Deklaratywne partycjonowanie w PostgreSQL upraszcza to zadanie: wystarczy zdefiniować partycję RANGE dla kolumny occurred_at, a baza danych będzie automatycznie kierować do niej wstawiane dane.
CREATE TABLE account_events_2025
PARTITION OF account_events
FOR VALUES FROM ('2025-01-01') TO ('2026-01-01');
CREATE TABLE account_events_2026
PARTITION OF account_events
FOR VALUES FROM ('2026-01-01') TO ('2027-01-01');Sprawdzenie wiedzy: tabele append-only
Sprawdź swoją wiedzę na temat projektowania tabel zdarzeń append-only.
Podsumowanie: tabele zdarzeń append-only
W tej lekcji dowiedział się Pan lub dowiedziała się Pani, jak projektować i używać tabel zdarzeń append-only:
- Niezmienność — wiersze są wstawiane raz i nigdy nie są modyfikowane. Zdarzenia z przeszłości są faktami.
- Kompletna historia — każda zmiana stanu jest zachowywana, co umożliwia tworzenie pełnych ścieżek audytu i wykonywanie zapytań na określony moment.
- Zdarzenia kompensujące — błędy naprawia się przez dodanie nowego zdarzenia odwracającego, a nie przez usunięcie starego.
- Wymuszanie niezmienności — reguły lub wyzwalacze na poziomie bazy danych zapobiegają przypadkowym modyfikacjom.
- Skalowalność — partycjonowanie zakresami zapewnia wydajne działanie dużych dzienników zdarzeń w miarę upływu czasu.
Tabele append-only stanowią podstawę event sourcingu, architektur CQRS i każdego systemu, w którym kluczowe znaczenie mają możliwość przeprowadzenia audytu oraz dokładność danych historycznych.
Często zadawane pytania
Czy lekcja „Tabele zdarzeń tylko do dopisywania” jest bezpłatna?
Tak — pełny tekst „Tabele zdarzeń tylko do dopisywania” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu SQL Academy, przejdź na CoddyKit PRO. Kurs SQL Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Tabele zdarzeń tylko do dopisywania”?
Rejestruj, co się wydarzyło, i nigdy nie nadpisuj danych Ćwiczysz SQL Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć SQL Academy?
Nie wymagamy żadnego doświadczenia. SQL Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.
Ile czasu zajmuje lekcja „Tabele zdarzeń tylko do dopisywania”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji SQL Academy?
Tak. Każda lekcja SQL Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Dlaczego warto przechowywać historię
- Tabele zdarzeń tylko do dopisywania
- Wiersze czasowe i wersjonowane
- Odtwarzanie stanu ze zdarzeń