Comment valider les données dans SQL Server

Un guide pratique pour valider la qualité des données dans Microsoft SQL Server — les six contrôles dont chaque table a besoin, le T-SQL pour les écrire, et comment en faire un contrat de données versionné qui s'exécute selon une planification.

· 9 min read

Le moyen le plus rapide de valider des données dans SQL Server consiste à exprimer chaque attente sous la forme d'une requête d'agrégation unique qui renvoie un nombre de violations, à les exécuter toutes sur la table en une seule passe, et à faire échouer le lot dès qu'un compteur franchit son seuil. Six contrôles couvrent l'essentiel des défaillances réelles : les valeurs nulles dans les colonnes obligatoires, les clés en doublon, les valeurs hors d'un ensemble autorisé, les nombres hors d'une plage plausible, les chaînes mal formées et les lignes périmées. Ce guide présente le T-SQL de chacun, les particularités de SQL Server qui vous piégeront, et la façon de passer de scripts ponctuels à un contrat qui s'exécute selon une planification.

Pourquoi SQL Server mérite une approche à part

La plupart des conseils sur la qualité des données sont écrits pour Postgres et supposent, sans le dire, des fonctionnalités que T-SQL n'a pas. Trois différences comptent :

Tout ce qui suit tient compte de ces points. Pour les aspects identiques sur tous les moteurs — où exécuter les contrôles, comment les planifier, quelles tables traiter en priorité — commencez par le guide complet de la validation des données.

Les six contrôles dont chaque table a besoin

1. Complétude — valeurs nulles dans les colonnes obligatoires

SELECT COUNT_BIG(*) AS violations
FROM [sales].[orders]
WHERE [order_id] IS NULL;

Utilisez COUNT_BIG plutôt que COUNT sur les grandes tables de faits : COUNT renvoie un int et déborde au-delà de 2,1 milliards de lignes. Notez aussi que COUNT(colonne) ignore déjà les valeurs nulles, d'où l'écriture du contrôle sous la forme d'un COUNT(*) filtré — les deux sont faciles à confondre, et la mauvaise version renvoie toujours zéro violation.

2. Unicité — clés métier en doublon

SELECT COUNT_BIG(*) AS violations
FROM (
    SELECT [order_id]
    FROM [sales].[orders]
    WHERE [order_id] IS NOT NULL
    GROUP BY [order_id]
    HAVING COUNT(*) > 1
) AS dupes;

Une contrainte de clé primaire empêcherait le problème, mais les tables analytiques sont généralement chargées par un traitement ELT dans un tas (heap) dépourvu de toute contrainte. Le contrôle de doublons est le prix d'une contrainte manquante.

3. Conformité — valeurs hors de l'ensemble autorisé

SELECT COUNT_BIG(*) AS violations
FROM [sales].[orders]
WHERE [status] IS NOT NULL
  AND [status] NOT IN ('pending', 'paid', 'shipped', 'refunded');

Gardez la condition IS NOT NULL. Avec une valeur nulle à gauche, NOT IN s'évalue à UNKNOWN, la ligne est écartée, et une colonne nulle à 40 % paraît parfaitement conforme.

4. Exactitude — nombres hors d'une plage plausible

SELECT COUNT_BIG(*) AS violations
FROM [sales].[orders]
WHERE [total_amount] IS NOT NULL
  AND ([total_amount] < 0 OR [total_amount] > 100000);

Les contrôles de plage attrapent les erreurs d'unité — des centimes chargés comme des euros, une décimale décalée par une conversion de devise — qu'aucune contrainte de schéma ne verra jamais.

5. Conformité — identifiants mal formés

C'est ici que T-SQL diverge. Il n'y a pas d'opérateur ~ : un motif ancré doit donc s'exprimer avec LIKE.

-- ^ORD-[0-9]{6}$  becomes:
SELECT COUNT_BIG(*) AS violations
FROM [sales].[orders]
WHERE [reference] IS NOT NULL
  AND [reference] NOT LIKE 'ORD-[0-9][0-9][0-9][0-9][0-9][0-9]';

LIKE est implicitement ancré aux deux extrémités lorsque le motif ne commence ni ne finit par % : c'est donc un véritable équivalent de l'expression régulière ancrée. Ce que vous ne pouvez pas exprimer, ce sont les alternatives, les groupes optionnels, les références arrière ou les répétitions non bornées. Pour ces cas, repliez-vous sur une liste explicite de valeurs autorisées ou sur un contrôle customSql écrit à la main, plutôt que de faire comme si LIKE était un moteur d'expressions régulières.

Catalyst effectue cette traduction pour vous : une règle regex sur une connexion SQL Server est convertie en motif LIKE équivalent lorsque c'est exprimable, et lève une erreur explicite « non pris en charge par ce dialecte » lorsque ce ne l'est pas — de sorte que le contrôle échoue bruyamment au lieu de passer en silence.

6. Actualité — fraîcheur

SELECT COUNT_BIG(*) AS violations
FROM [sales].[orders]
WHERE [created_at] < DATEADD(HOUR, -24, SYSUTCDATETIME());

Utilisez SYSUTCDATETIME(), pas GETDATE(). GETDATE() renvoie l'heure locale du serveur : le même contrôle dérive donc d'une heure deux fois par an, et de plusieurs heures entières d'une région à l'autre. Stockez les horodatages en datetime2 et comparez en UTC.

Créer une connexion en lecture seule sans risque

La validation ne devrait jamais avoir besoin d'un accès en écriture. Une connexion minimale ressemble à ceci :

CREATE LOGIN catalyst_ro WITH PASSWORD = '<generated>';
CREATE USER catalyst_ro FOR LOGIN catalyst_ro;
ALTER ROLE db_datareader ADD MEMBER catalyst_ro;
GRANT VIEW DEFINITION TO catalyst_ro;   -- needed to read INFORMATION_SCHEMA

db_datareader accorde le SELECT sur toutes les tables ; VIEW DEFINITION est ce qui permet au compte d'énumérer les colonnes et les types depuis INFORMATION_SCHEMA.COLUMNS. Pointez-la vers un réplica secondaire lisible si vous disposez d'un groupe de disponibilité — les requêtes de validation sont des balayages d'agrégation et n'ont rien à faire sur l'instance primaire.

Du SQL ponctuel au contrat de données

Des scripts comme ceux ci-dessus se dégradent. Ils vivent dans le dépôt de quelqu'un, plus personne ne sait lesquels tournent encore, et les seuils restent invisibles pour les analystes qui en dépendent. La solution consiste à exprimer les attentes de manière déclarative, dans un format lisible à la fois par des humains et par des outils.

Catalyst s'appuie pour cela sur l'Open Data Contract Standard (ODCS). Les six contrôles ci-dessus deviennent :

apiVersion: v3.0.0
kind: DataContract
info:
  title: orders
  version: 1.2.0
  owner: data-platform
schema:
  - name: orders
    physicalName: orders
    physicalType: table
    properties:
      - name: order_id
        logicalType: string
        physicalType: uniqueidentifier
        required: true
        primaryKey: true
        quality:
          - rule: nullCount
            dimension: completeness
            severity: error
            mustBe: "0"
          - rule: duplicateCount
            dimension: uniqueness
            severity: error
            mustBe: "0"
      - name: status
        logicalType: string
        quality:
          - rule: validValues
            dimension: conformity
            severity: error
            mustBe: "['pending', 'paid', 'shipped', 'refunded']"
      - name: total_amount
        logicalType: number
        quality:
          - rule: between
            dimension: accuracy
            severity: error
            mustBe: "[0, 100000]"
      - name: reference
        logicalType: string
        quality:
          - rule: regex
            dimension: conformity
            severity: warning
            mustBe: "'^ORD-[0-9]{6}
#39;" - name: created_at logicalType: timestamp quality: - rule: freshness dimension: timeliness severity: error mustBe: "<= 24h"

Le YAML fait foi. Catalyst compile chaque entrée quality en T-SQL conforme au dialecte, tel que montré plus haut, exécute le lot sur votre serveur et consigne un résultat pass / warn / fail par contrôle. Modifier une règle dans l'éditeur visuel réécrit ce même YAML, commentaires et ordre préservés : le contrat relu dans une pull request est donc bien le contrat qui s'exécute.

Notez le champ severity. error fait échouer l'exécution ; warning consigne la violation et poursuit. Les contrôles de motif sur des champs saisis par des humains sont généralement des avertissements — ce que vous voulez, c'est la tendance, pas une astreinte à 3 h du matin.

Planification et alertes

Une validation que vous lancez à la main est une validation que vous ne lancez qu'une fois. Attachez une planification au jeu de données — toutes les heures pour la fraîcheur d'une table en flux, quotidiennement après la fenêtre ELT pour le reste — et alertez sur la transition, pas sur l'état. Ce qui compte, c'est « orders vient de passer en échec », pas « orders est toujours en échec », qui est précisément ce qui apprend aux gens à ignorer le canal.

Deux remarques opérationnelles propres à SQL Server :

Les pièges SQL Server à connaître

PiègeCe qui se passeQue faire
Débordement de COUNT(*)Échec au-delà de 2,1 milliards de lignesUtiliser COUNT_BIG(*)
NOT IN avec des valeurs nullesLignes silencieusement excluesAjouter IS NOT NULL
GETDATE()Heure locale du serveurUtiliser SYSUTCDATETIME()
Classement insensible à la casse'PAID' passe un contrôle sur 'paid'Fixer le classement, ou normaliser avec COLLATE
Conversion impliciteWHERE varchar_col = 123 balaie toute la tableComparer des types identiques
Égalité sur des floatBornes de plage faussées par l'arrondiUtiliser decimal pour les montants

Questions fréquentes

Puis-je valider des données dans SQL Server sans écrire de SQL ?

Oui. Définissez l'attente de manière déclarative — obligatoire, unique, valeurs autorisées, plage numérique, motif, âge maximal — et laissez l'outil la compiler en T-SQL. Catalyst importe les colonnes et les types depuis INFORMATION_SCHEMA, suggère un jeu de règles de référence à partir du schéma, et génère les requêtes. Vous n'écrivez du SQL que pour la logique véritablement sur mesure, via une règle customSql.

La validation de la qualité des données a-t-elle besoin d'un accès en écriture à ma base ?

Non. Chaque contrôle de ce guide est un SELECT qui renvoie un seul nombre. Une connexion db_datareader assortie de VIEW DEFINITION suffit. Catalyst se connecte en lecture seule et ne stocke que des métadonnées et des résultats de contrôles — les lignes elles-mêmes restent sur votre serveur.

Comment vérifier des expressions régulières en T-SQL ?

Directement, vous ne le pouvez pas : SQL Server n'a pas d'opérateur d'expression régulière. Les motifs ancrés de longueur fixe se traduisent en LIKE avec des classes de caractères de type [0-9]. Tout ce qui utilise des alternatives, des groupes optionnels ou des répétitions non bornées doit devenir une liste de valeurs autorisées, une fonction CLR ou un contrôle SQL personnalisé.

À quelle fréquence exécuter les validations ?

Alignez la planification sur la cadence propre des données, et lancez le contrôle juste après le chargement qui les produit. Les règles de fraîcheur sur des tables en flux appellent des exécutions horaires ; les tables de dimensions en batch n'en demandent qu'une, après la fin de l'ELT nocturne. Valider plus souvent que les données ne changent ne produit que du bruit.

Est-ce que cela fonctionne avec Azure SQL et Microsoft Fabric ?

Azure SQL Database expose la même surface T-SQL : tout ce qui précède s'applique sans changement. L'entrepôt de Microsoft Fabric et son point de terminaison SQL analytics parlent eux aussi T-SQL, avec la même absence d'expressions régulières — voyez le guide Microsoft Fabric pour les différences autour de l'authentification Entra ID et de la synchronisation des métadonnées OneLake.