0Pricing
SQL Academy · Leçon

Tables d’événements en ajout uniquement

Enregistrez ce qui s’est produit sans jamais écraser les données.

Tables d’événements en ajout uniquement est une leçon SQL Academy gratuite sur CoddyKit. Ceci est la leçon 2 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage SQL Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours SQL Academy comprend 4 leçons au total.

Qu’est-ce qu’une table en ajout uniquement ?

Une table d’événements en ajout uniquement est une table dans laquelle les lignes sont uniquement insérées, jamais mises à jour ni supprimées. Chaque ligne représente quelque chose qui s’est produit à un instant précis.

Ce modèle est à la base de la reconstitution de l’état à partir d’événements. Au lieu de stocker l’état actuel, vous stockez chaque modification sous forme d’événement immuable, ce qui vous fournit un historique complet et auditable.

Créer une table d’événements

Une table d’événements bien conçue indique qui a fait quoi, sur quelle ressource et quand. La colonne occurred_at enregistre l’horodatage exact, et DEFAULT NOW() garantit qu’elle est toujours renseignée automatiquement.

Remarquez qu’il n’y a ni UPDATE ni DELETE dans cette conception : une fois écrites, les lignes sont permanentes.

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

Insérer des événements

Chaque action d’un utilisateur — se connecter, déposer de l’argent, modifier une adresse e-mail — devient une nouvelle ligne. Vous ne revenez jamais modifier un événement précédent. Si une correction est nécessaire, vous insérez plutôt un événement compensatoire.

La séquence complète des événements est ainsi préservée, dans l’ordre où ils se sont produits.

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

Lire l’historique complet

Comme chaque changement d’état est stocké sous forme de ligne, interroger l’historique complet d’un compte revient à effectuer un simple SELECT ordonné par date. Vous pouvez rejouer toute la vie d’un enregistrement, du tout premier événement au plus récent.

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

Reconstituer l’état actuel

Avec une table en ajout uniquement, vous ne stockez pas directement l’état actuel : vous le déduisez en lisant le dernier événement pertinent. Ici, le forfait actuel du compte 42 correspond à ce qu’indique l’événement le plus récent de changement de forfait ou d’ouverture du compte.

Utiliser ORDER BY occurred_at DESC LIMIT 1 permet de récupérer efficacement l’instantané le plus récent.

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;

Garantir l’immutabilité avec des règles

La garantie d’une table en ajout uniquement peut être appliquée au niveau de la base de données à l’aide d’une RULE qui ignore silencieusement tout UPDATE ou DELETE sur la table. Cela empêche les modifications accidentelles provenant de toute application disposant d’un accès en écriture.

Un déclencheur qui lève une exception constitue une solution encore plus stricte, car il rejette activement l’opération avec une erreur.

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;

Garantir l’immutabilité avec un déclencheur

Un déclencheur qui lève une exception est plus strict qu’une règle silencieuse : l’application reçoit immédiatement une erreur si elle tente de modifier un événement passé. Les bogues deviennent ainsi visibles au lieu d’être ignorés silencieusement.

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

Compter les événements au fil du temps

Les tables en ajout uniquement facilitent les analyses temporelles. Comme chaque événement possède un horodatage, vous pouvez regrouper les données par jour, semaine ou mois sans ajouter de colonnes. L’exemple ci-dessous compte le nombre d’événements de chaque type survenus chaque jour.

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

Reconstituer un état à un instant donné

L’une des propriétés les plus puissantes d’un journal d’événements est la possibilité de reconstituer l’état de n’importe quel enregistrement tel qu’il existait à un instant passé. Il suffit de filtrer les événements jusqu’à l’horodatage souhaité : aucune extension de voyage dans le temps n’est nécessaire.

Cette possibilité est extrêmement utile pour le débogage, les audits et la conformité réglementaire.

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

Des événements compensatoires plutôt que des corrections

Lorsqu’une erreur est découverte, par exemple une facturation incorrecte, vous ne supprimez pas l’événement erroné. Vous insérez plutôt un événement compensatoire qui l’annule ou l’inverse. Les deux événements restent visibles dans le journal, indiquant exactement ce qui s’est passé et quand la correction a été effectuée.

La piste d’audit reste ainsi complète et permet de détecter toute altération.

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

Partitionner les grandes tables d’événements

Les tables d’événements grossissent rapidement. Le partitionnement par plage temporelle maintient les partitions individuelles à une taille réduite, accélère les requêtes sur des plages et permet d’archiver ou de supprimer les anciennes partitions sans toucher aux données récentes.

Le partitionnement déclaratif de PostgreSQL simplifie cette opération : définissez une partition RANGE sur occurred_at et laissez la base de données acheminer automatiquement les insertions.

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

Vérification des connaissances sur les tables en ajout uniquement

Vérifiez votre compréhension de la conception des tables d’événements en ajout uniquement.

Récapitulatif : tables d’événements en ajout uniquement

Dans cette leçon, vous avez appris à concevoir et à utiliser des tables d’événements en ajout uniquement :

  • Immutabilité : les lignes sont insérées une seule fois et ne sont jamais modifiées. Les événements passés sont des faits.
  • Historique complet : chaque changement d’état est conservé, ce qui permet de disposer de pistes d’audit complètes et d’effectuer des requêtes à un instant donné.
  • Événements compensatoires : les erreurs sont corrigées en ajoutant un nouvel événement d’annulation, et non en supprimant l’ancien.
  • Application des règles : les règles ou les déclencheurs au niveau de la base de données empêchent les modifications accidentelles.
  • Évolutivité : le partitionnement par plage permet aux grands journaux d’événements de rester performants au fil du temps.

Les tables en ajout uniquement sont la colonne vertébrale de la reconstitution de l’état à partir d’événements, des architectures CQRS et de tout système où la possibilité d’audit et l’exactitude historique sont essentielles.

Questions Fréquemment Posées

La leçon « Tables d’événements en ajout uniquement » est-elle gratuite ?

Oui — le texte complet de « Tables d’événements en ajout uniquement » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours SQL Academy, passe à CoddyKit PRO. Le cours SQL Academy comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Tables d’événements en ajout uniquement » ?

Enregistrez ce qui s’est produit sans jamais écraser les données. Tu pratiques SQL Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer SQL Academy ?

Aucune expérience préalable n'est requise. SQL Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 2 sur 4.

Combien de temps prend la leçon « Tables d’événements en ajout uniquement » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon SQL Academy ?

Oui. Chaque leçon SQL Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. Pourquoi conserver l’historique
  2. Tables d’événements en ajout uniquement
  3. Lignes temporelles et versionnées
  4. Reconstituer l’état à partir des événements
← Retour à SQL Academy