clement13.net
Photo d’origine : Un centre de données vu du toit
Description
Un centre de données vu du toit
Source
Rsparks3 · page d’origine
Licence
CC0 Domaine public
Photo d’origine, non retouchée. L’arrière-plan en est une version stylisée par le site.

La programmation ensembliste en T-SQL

Décrire le résultat, pas le chemin : la manière de penser qui change l'échelle de ce que le SQL sait traiter.

≈ 3 min de lectureLecture
En bref
  • Objectif : exprimer le résultat attendu et laisser l'optimiseur choisir l'exécution.
  • Principe : des ensembles en entrée, un ensemble en sortie, composés entre eux.
  • Moyens : jointures, agrégats, fonctions de fenêtre, CTE, APPLY, écritures ensemblistes.
  • Garde-fou : une boucle sur des objets est légitime ; sur des lignes de données, rarement.
Sur cette page
Deux moitiés

Cette section traite d'abord de l'usage du moteur (principes, moyens, exemples). L'autre moitié est une démarche : préparer le modèle avant, puis poser très peu de très grosses requêtes. Voir La démarche, les erreurs de conception et les cas catastrophiques.

Objectif#

La programmation ensembliste consiste à exprimer le résultat que l'on veut (un ensemble de lignes défini par des propriétés), plutôt que la suite d'étapes qui le fabrique ligne après ligne. En T-SQL, cela revient à confier à l'optimiseur de requêtes le choix de la méthode d'exécution.

Ses bénéfices, quand on la pratique bien :

  • Performance : une instruction traite des milliers de lignes en un passage, avec parallélisme, algorithmes de jointure adaptés et écritures groupées.
  • Lisibilité : l'intention (« quoi ») est visible ; la mécanique (« comment ») est laissée au moteur.
  • Fiabilité : une instruction est atomique ; pas d'état intermédiaire à gérer à la main.
  • Maintenabilité : moins de variables, moins de branches, moins d'effets de bord.
  • Passage à l'échelle : le coût croît le plus souvent de façon quasi linéaire avec le volume, au lieu d'exploser.

Le principe en cinq idées#

  1. Une table est un ensemble de lignes, sans ordre. Aucune ligne n'est « la première » tant qu'on n'a pas demandé un tri.
  2. Chaque opération prend des ensembles et rend un ensemble. On peut donc les composer : filtrer, joindre, regrouper, puis réutiliser le résultat.
  3. On décrit la propriété du résultat (« les commandes de plus de deux ans »), pas l'algorithme pour les trouver.
  4. L'optimiseur choisit le plan d'exécution à partir des statistiques et des index.
  5. On pense « grain » : à quoi correspond une ligne du résultat (une vente, un client par mois, un jour) ? Presque toutes les erreurs viennent d'un grain mal défini.

Procédural et ensembliste, d'un coup d'œil#

AspectProcédural (ligne à ligne)Ensembliste
RaisonnementÉtapes, boucles, variables, états intermédiairesRelations, jointures, filtres, regroupements
Ordre d'exécutionDécidé par le programmeurDécidé par l'optimiseur
Coût par ligneUne instruction, un contexte d'exécution et souvent une écriture au journal par ligneAmorti sur l'ensemble : lecture et écriture groupées
ParallélismeTrès limitéPossible, géré par le moteur
VolumétrieDégradation rapideCroissance en général quasi linéaire
Cas d'usage typiqueOrchestration, appels par objet, effets externesTransformation de données, calculs, nettoyage, chargement

Un exemple minimal#

Marquer comme archivées les commandes antérieures à 2020 :

Procédural#

-- Procédural : marquer les commandes anciennes, une par une
DECLARE @Id int;
DECLARE c CURSOR LOCAL FAST_FORWARD FOR
    SELECT CommandeId FROM dbo.Commandes WHERE DateCommande < '2020-01-01';
OPEN c;
FETCH NEXT FROM c INTO @Id;
WHILE @@FETCH_STATUS = 0
BEGIN
    UPDATE dbo.Commandes SET Statut = 'ARCHIVEE' WHERE CommandeId = @Id;
    FETCH NEXT FROM c INTO @Id;
END;
CLOSE c;
DEALLOCATE c;

Ensembliste#

-- Ensembliste : une seule instruction, un seul plan
UPDATE dbo.Commandes
SET    Statut = 'ARCHIVEE'
WHERE  DateCommande < '2020-01-01';

La seconde forme exprime la même chose en une instruction. Le moteur peut utiliser un index sur DateCommande, regrouper les écritures dans le journal de transactions et, si la volumétrie le justifie, paralléliser. La première forme exécute autant d'instructions que de lignes : à chaque itération, une ligne est lue, une instruction est lancée, une ligne est modifiée.

Pourquoi la boucle coûte cher

Quand elle n'est pas dans une transaction explicite, chaque instruction est validée séparément (mode autocommit) : le moteur doit attendre l'écriture du journal à chaque ligne. Voir le détail du coût.

Plan de cette section#

Principes en détailModèle relationnel, optimiseur, coût du traitement ligne à ligne, NULL, déterminisme et transactions : les fondations de la programmation ensembliste en T-SQL.Les moyens du T-SQLJointures, fonctions de fenêtre, CTE, APPLY, écritures ensemblistes, séries et traitement par lots : la boîte à outils T-SQL de la programmation ensembliste, avec les versions de SQL Server.Déterminisme et objets déterministesObjets déterministes en T-SQL : colonnes calculées et vues indexées, fonctions liées au schéma, repliement de constantes, inlining, et types table pour figer le contexte d'exécution d'une procédure.Générer du code : inlining et valeurs en durCompiler du code T-SQL à la volée pour écrire en dur le résultat d'un SELECT de référence : estimations de cardinalité, index filtrés, fonctions déterministes, inlining, risques et bonnes pratiques.La démarche : préparer, puis frapper une foisLa démarche ensembliste : préparer le modèle (périmètre, index, fonctions pures, dérivées matérialisées, entrées figées) avant de poser très peu de très grosses requêtes, sans itération, dans un traitement entièrement déterministe.Les erreurs de conception d'un expertQuinze erreurs que commet même un développeur qui connaît parfaitement le moteur SQL : boucle externe invisible, fonction scalaire, dérivées recalculées, contexte qui bouge, estimations faussées. Pour chacune, une méthode.Exemples : procédural contre ensemblisteSix exemples T-SQL comparant une version procédurale (curseur, boucle) et une version ensembliste : cumul, mise à jour, dédoublonnage, agrégation de texte, calendrier, plages continues.6 pagesCas catastrophiques : le SQL est propre, l'assemblage est fauxTrois traitements T-SQL dont chaque requête est propre mais qui cumulent plusieurs erreurs d'assemblage, mesurés, puis refondus par la démarche ensembliste : préparation, très peu de requêtes, aucune itération.3 pagesQuand le procédural est légitimeCas où une boucle est justifiée en T-SQL (objets, lots, effets externes) et faux ensemblistes à éviter (fonctions scalaires, curseurs déguisés, déclencheurs ligne à ligne).Mesurer sérieusementMéthode et script T-SQL reproductible pour comparer un traitement procédural et sa version ensembliste : CPU, lectures, volumes croissants, égalité des résultats.
Conventions et prérequis

Les exemples utilisent des tables fictives du schéma dbo. Les versions de SQL Server requises sont indiquées à côté de chaque fonctionnalité (par exemple SQL Server 2017+). À exécuter sur une base de test : plusieurs scripts créent des tables et suppriment des lignes.

Sources et vérification#