0Pricing
SQL Academy · Leçon

Lignes temporelles et versionnées

Requêtes selon la période de validité et à une date donnée.

Lignes temporelles et versionnées est une leçon SQL Academy gratuite sur CoddyKit. Ceci est la leçon 3 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.

Que sont les tables temporelles ?

Les tables temporelles permettent de suivre l’évolution des données au fil du temps. Au lieu d’écraser une ligne lorsqu’une modification survient, une table temporelle conserve chaque version de cette ligne, associée à la période pendant laquelle elle était valide.

Deux concepts sont essentiels : le temps de validité, c’est-à-dire le moment où le fait était vrai dans le monde réel, et le temps de transaction, c’est-à-dire le moment où la base de données a enregistré ce fait. Leur combinaison produit une table entièrement bitemporelle.

Temps de validité et temps de transaction

Le temps de validité correspond à la période durant laquelle un fait est vrai dans le monde réel, par exemple le salaire d’un employé du 2020-01-01 au 2022-06-30. Le temps de transaction correspond au moment où la ligne a été insérée ou est arrivée à expiration dans la base de données. Ensemble, ils répondent à deux questions : Qu’est-ce qui était vrai ? et Quand l’avons-nous su ?

La plupart des cas d’usage commencent par le suivi du temps de validité, que vous pouvez mettre en œuvre manuellement à l’aide des colonnes valid_from et valid_to.

Créer une table à temps de validité

La manière la plus simple de stocker des lignes versionnées consiste à ajouter des colonnes d’horodatage valid_from et valid_to. Une valeur NULL dans valid_to (ou une valeur sentinelle très éloignée dans le futur, comme 9999-12-31) indique que la ligne est actuellement active.

CREATE TABLE employee_salary (
  id          SERIAL PRIMARY KEY,
  employee_id INT NOT NULL,
  salary      NUMERIC(12, 2) NOT NULL,
  valid_from  DATE NOT NULL,
  valid_to    DATE
);

INSERT INTO employee_salary (employee_id, salary, valid_from, valid_to)
VALUES
  (1, 50000, '2020-01-01', '2022-06-30'),
  (1, 60000, '2022-07-01', NULL);

Interroger la version actuelle

Pour trouver la ligne actuellement active de chaque employé, filtrez les lignes où valid_to IS NULL (période ouverte) ou celles dont la date du jour se situe dans la plage de validité. Utiliser une valeur sentinelle comme '9999-12-31' simplifie les comparaisons de plages.

SELECT employee_id, salary
FROM employee_salary
WHERE valid_to IS NULL
ORDER BY employee_id;

Requêtes à une date donnée

Une requête à une date donnée pose la question suivante : Quelles étaient les données à un instant précis ? Vous filtrez les lignes dont la période de validité contient l’horodatage fourni. C’est l’une des fonctionnalités les plus puissantes des tables temporelles.

-- What was employee 1's salary on 2021-03-15?
SELECT employee_id, salary, valid_from, valid_to
FROM employee_salary
WHERE employee_id = 1
  AND valid_from <= '2021-03-15'
  AND (valid_to IS NULL OR valid_to > '2021-03-15');

Mettre à jour une ligne versionnée

Lorsqu’un fait change, vous ne faites pas de UPDATE sur la ligne existante. Vous fermez plutôt la ligne actuelle en définissant sa valeur valid_to, puis vous insérez une nouvelle ligne avec la nouvelle valeur. L’historique complet est ainsi préservé.

-- Employee 1 gets a raise effective 2023-01-01
BEGIN;

-- Close the current open row
UPDATE employee_salary
SET valid_to = '2022-12-31'
WHERE employee_id = 1
  AND valid_to IS NULL;

-- Insert the new version
INSERT INTO employee_salary (employee_id, salary, valid_from, valid_to)
VALUES (1, 72000, '2023-01-01', NULL);

COMMIT;

Utiliser une plage de dates pour les périodes de validité

Le type daterange de PostgreSQL modélise élégamment une période de validité dans une seule colonne. Vous pouvez utiliser l’opérateur @> (contient) pour vérifier qu’une date se trouve dans la plage et ajouter une contrainte d’exclusion afin d’empêcher le chevauchement des périodes pour une même entité.

CREATE TABLE employee_salary_v2 (
  id          SERIAL PRIMARY KEY,
  employee_id INT NOT NULL,
  salary      NUMERIC(12, 2) NOT NULL,
  valid_period DATERANGE NOT NULL,
  EXCLUDE USING GIST (employee_id WITH =, valid_period WITH &&)
);

INSERT INTO employee_salary_v2 (employee_id, salary, valid_period)
VALUES
  (1, 50000, '[2020-01-01, 2022-07-01)'),
  (1, 60000, '[2022-07-01, infinity)');

Requête à une date donnée avec une plage de dates

Avec l’approche fondée sur daterange, la requête à une date donnée devient très lisible. L’opérateur @> vérifie que la date fournie est contenue dans la plage, en gérant automatiquement les bornes inférieure et supérieure.

-- What was employee 1's salary on 2021-03-15?
SELECT employee_id, salary, valid_period
FROM employee_salary_v2
WHERE employee_id = 1
  AND valid_period @> '2021-03-15'::date;

Tables temporelles versionnées par le système (norme SQL)

La norme SQL:2011 a introduit les tables temporelles versionnées par le système. La base de données gère automatiquement les colonnes de temps de transaction row_start et row_end. Dans PostgreSQL, vous devez simuler ce comportement ; dans SQL Server et MariaDB, il est intégré avec SYSTEM VERSIONING.

L’exemple ci-dessous présente la syntaxe de SQL Server / MariaDB comme référence pour illustrer le concept.

-- SQL Server / MariaDB syntax (reference)
CREATE TABLE dbo.Product (
  ProductID   INT PRIMARY KEY,
  Name        VARCHAR(100),
  Price       DECIMAL(10,2),
  SysStart    DATETIME2 GENERATED ALWAYS AS ROW START,
  SysEnd      DATETIME2 GENERATED ALWAYS AS ROW END,
  PERIOD FOR SYSTEM_TIME (SysStart, SysEnd)
)
WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.Product_History));

Jointures temporelles : aligner deux tables dans le temps

Une difficulté courante consiste à joindre deux tables temporelles sur des périodes correspondantes. Par exemple, vous pouvez joindre les salaires des employés aux affectations de service lorsque les deux possèdent des périodes de validité. La jointure s’effectue sur la clé de l’entité AND la condition de chevauchement, à l’aide de && sur les plages ou de comparaisons explicites de dates.

CREATE TABLE dept_assignment (
  employee_id INT,
  department  VARCHAR(50),
  valid_period DATERANGE
);

INSERT INTO dept_assignment VALUES
  (1, 'Engineering', '[2020-01-01, infinity)'),
  (1, 'Marketing',   '[2019-01-01, 2020-01-01)');

-- Periods where employee 1 was in Engineering AND had salary > 55000
SELECT s.salary, d.department,
       s.valid_period * d.valid_period AS overlap_period
FROM employee_salary_v2 s
JOIN dept_assignment d
  ON s.employee_id = d.employee_id
  AND s.valid_period && d.valid_period
WHERE s.employee_id = 1
  AND s.salary > 55000;

Éviter les lacunes et les chevauchements

Deux problèmes courants de qualité des données dans les tables temporelles sont les lacunes (périodes sans enregistrement) et les chevauchements (deux lignes valides simultanément). La contrainte d’exclusion avec && empêche les chevauchements au niveau de la base de données. La détection des lacunes nécessite de rechercher les zones non couvertes au moyen d’une requête.

-- Find gaps in salary history for employee 1
-- (periods where upper(prev) < lower(next))
SELECT
  upper(a.valid_period) AS gap_start,
  lower(b.valid_period) AS gap_end
FROM employee_salary_v2 a
JOIN employee_salary_v2 b
  ON a.employee_id = b.employee_id
  AND upper(a.valid_period) < lower(b.valid_period)
WHERE a.employee_id = 1
  AND NOT EXISTS (
    SELECT 1 FROM employee_salary_v2 c
    WHERE c.employee_id = 1
      AND lower(c.valid_period) > upper(a.valid_period)
      AND lower(c.valid_period) < lower(b.valid_period)
  )
ORDER BY gap_start;

Vérification des connaissances

Vérifiez votre compréhension des tables temporelles et des requêtes à une date donnée.

Récapitulatif : lignes temporelles et versionnées

Dans cette leçon, vous avez appris à modéliser les données qui évoluent dans le temps à l’aide de colonnes de temps de validité et du type daterange de PostgreSQL. Points essentiels :

  • N’écrasez jamais les lignes historiques : fermez l’ancienne et insérez une nouvelle version.
  • Utilisez des requêtes à une date donnée (valid_from <= target AND valid_to > target) pour récupérer les données à n’importe quel instant passé.
  • Le type daterange associé à l’opérateur @> rend les requêtes temporelles concises et lisibles.
  • Les contraintes d’exclusion sur && (chevauchement de plages) garantissent l’intégrité des données au niveau de la base de données.
  • Les jointures temporelles alignent deux historiques en faisant se chevaucher leurs périodes de validité.

Ces modèles constituent le fondement de la reconstitution de l’état à partir d’événements, de la journalisation d’audit et de tout système où l’exactitude historique est importante.

Questions Fréquemment Posées

La leçon « Lignes temporelles et versionnées » est-elle gratuite ?

Oui — le texte complet de « Lignes temporelles et versionnées » 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 « Lignes temporelles et versionnées » ?

Requêtes selon la période de validité et à une date donnée. 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 3 sur 4.

Combien de temps prend la leçon « Lignes temporelles et versionnées » ?

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