SQL Academy · Lektion

Hændelsestabeller, der kun tilføjes til

Registrér, hvad der skete, og overskriv aldrig

Lektion 2 af 413 trin

Hændelsestabeller, der kun tilføjes til er en gratis SQL Academy-lektion på CoddyKit. Dette er lektion 2 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i SQL Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. SQL Academy-kurset indeholder 4 lektioner i alt.

Hvad er en tabel med kun tilføjelser

En hændelsestabel med kun tilføjelser er en tabel, hvor rækker kun indsættes — aldrig opdateres eller slettes. Hver række repræsenterer noget, der skete på et bestemt tidspunkt.

Dette mønster er grundlaget for event sourcing. I stedet for at gemme den aktuelle tilstand gemmer du alle ændringer som uforanderlige hændelser, så du får en komplet historik, der kan revideres.

Oprettelse af en hændelsestabel

En veldesignet hændelsestabel registrerer, hvem der gjorde hvad mod hvilken ressource, og hvornår. Kolonnen occurred_at registrerer det nøjagtige tidsstempel, og DEFAULT NOW() sikrer, at den altid udfyldes automatisk.

Bemærk, at dette design ikke indeholder UPDATE eller DELETE — rækker er permanente, når de først er skrevet.

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

Indsættelse af hændelser

Hver handling, en bruger foretager — at logge ind, indsætte penge eller ændre en e-mailadresse — bliver til en ny række. Du går aldrig tilbage og redigerer en tidligere hændelse. Hvis noget skal rettes, indsætter du i stedet en kompenserende hændelse.

Det bevarer hele rækkefølgen af det, der skete.

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

Læsning af hele historikken

Fordi hver tilstandsændring gemmes som en række, er det enkelt at hente hele historikken for en konto med en SELECT, der sorteres efter tid. Du kan afspille hele en posts liv fra den første hændelse til den seneste.

SELECT
  id,
  event_type,
  payload,
  occurred_at
FROM account_events
WHERE account_id = 42
ORDER BY occurred_at ASC;

Udledning af den aktuelle tilstand

Med en tabel med kun tilføjelser gemmer du ikke den aktuelle tilstand direkte — du udleder den ved at læse den seneste relevante hændelse. Her er den aktuelle plan for konto 42 det, som den seneste hændelse af typen plan_upgraded eller account_opened angiver.

Ved at bruge ORDER BY occurred_at DESC LIMIT 1 henter du effektivt det seneste øjebliksbillede.

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;

Håndhævelse af uforanderlighed med regler

Garantien om kun tilføjelser kan håndhæves på databaseniveau ved hjælp af en RULE, der lydløst ignorerer enhver UPDATE eller DELETE på tabellen. Det forhindrer utilsigtede ændringer fra alle programmer med skriveadgang.

En udløser, der rejser en undtagelse, er et endnu stærkere alternativ, fordi den aktivt afviser handlingen med en fejl.

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;

Uforanderlighed via en udløser

En udløser, der rejser en undtagelse, er strengere end en lydløs regel — programmet modtager straks en fejl, hvis det forsøger at ændre en tidligere hændelse. Det gør fejl synlige i stedet for lydløst at skjule dem.

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

Optælling af hændelser over tid

Tabeller med kun tilføjelser gør tidsbaseret analyse enkel. Da hver hændelse har et tidsstempel, kan du gruppere efter dag, uge eller måned uden ekstra kolonner. Eksemplet nedenfor tæller, hvor mange hændelser af hver type der fandt sted pr. dag.

SELECT
  DATE_TRUNC('day', occurred_at) AS day,
  event_type,
  COUNT(*)                        AS total
FROM account_events
GROUP BY 1, 2
ORDER BY 1, 2;

Rekonstruktion på et bestemt tidspunkt

En af de mest kraftfulde egenskaber ved en hændelseslog er muligheden for at rekonstruere en posts tilstand på et tidligere tidspunkt. Du skal blot filtrere hændelser frem til det ønskede tidsstempel — der kræves ingen tidsrejseudvidelse.

Det er uvurderligt til fejlfinding, revisioner og overholdelse af lovkrav.

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

Kompenserende hændelser i stedet for rettelser

Når en fejl opdages — for eksempel en forkert opkrævning — skal du ikke slette den forkerte hændelse. I stedet indsætter du en kompenserende hændelse, der annullerer eller tilbagefører den. Begge hændelser forbliver synlige i loggen, så det fremgår præcist, hvad der skete, og hvornår rettelsen blev foretaget.

Det holder revisionssporet komplet og manipulationssikkert.

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

Partitionering af store hændelsestabeller

Hændelsestabeller vokser hurtigt. Partitionering efter et tidsinterval holder de enkelte partitioner små, gør intervalforespørgsler hurtigere og gør det muligt at arkivere eller slette gamle partitioner uden at påvirke nyere data.

PostgreSQLs deklarative partitionering gør dette enkelt: Definér en RANGE-partition på occurred_at, og lad databasen dirigere indsættelser automatisk.

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

Videnstest: Tabeller med kun tilføjelser

Test din forståelse af design af hændelsestabeller med kun tilføjelser.

Opsummering: Hændelsestabeller med kun tilføjelser

I denne lektion lærte du at designe og bruge hændelsestabeller med kun tilføjelser:

  • Uforanderlighed — rækker indsættes én gang og ændres aldrig. Tidligere hændelser er fakta.
  • Komplet historik — hver tilstandsændring bevares, så det bliver muligt at føre et komplet revisionsspor og lave forespørgsler på bestemte tidspunkter.
  • Kompenserende hændelser — fejl rettes ved at tilføje en ny hændelse, der ophæver den tidligere, ikke ved at slette den gamle.
  • Håndhævelse — regler eller udløsere på databaseniveau forhindrer utilsigtede ændringer.
  • Skalerbarhed — intervalpartitionering holder store hændelseslogge effektive over tid.

Tabeller med kun tilføjelser er rygraden i event sourcing, CQRS-arkitekturer og alle systemer, hvor muligheden for revision og historisk nøjagtighed er afgørende.

Gratis at komme i gang

Lær SQL med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
46
Lektioner
183

Ofte stillede spørgsmål

Er lektionen “Hændelsestabeller, der kun tilføjes til” gratis?

Ja — hele teksten til “Hændelsestabeller, der kun tilføjes til” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af SQL Academy-kurset, skal du opgradere til CoddyKit PRO. SQL Academy-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “Hændelsestabeller, der kun tilføjes til”?

Registrér, hvad der skete, og overskriv aldrig Du øver dig i SQL Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på SQL Academy?

Der kræves ingen tidligere erfaring. SQL Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 2 af 4.

Hvor lang tid tager lektionen “Hændelsestabeller, der kun tilføjes til”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne SQL Academy-lektion?

Ja. Alle SQL Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. Hvorfor bevare historikken
  2. Hændelsestabeller, der kun tilføjes til
  3. Temporale og versionsstyrede rækker
  4. Genskab tilstand ud fra hændelser
← Tilbage til SQL Academy