Comment valider les données dans Amazon Redshift
Un guide pratique de la validation de la qualité des données dans Amazon Redshift — les six contrôles dont toute table a besoin, comment les tenir à l'écart de la file WLM, le piège des contraintes non appliquées, et comment les exécuter sous forme de contrat de données versionné.
· 9 min read
Pour valider les données dans Amazon Redshift, exprimez chaque attente sous forme de requête d'agrégation renvoyant un nombre de violations, et exécutez le lot après chaque chargement. Les six contrôles qui valent la peine sont : valeurs nulles dans les colonnes obligatoires, clés en double, valeurs hors de l'ensemble autorisé, nombres hors d'une plage plausible, chaînes malformées et lignes périmées. L'ascendance PostgreSQL de Redshift fait que l'essentiel du SQL est familier — mais deux choses ne le sont pas : ses contraintes sont déclarées et jamais appliquées, de sorte que l'optimiseur peut produire de mauvaises réponses à partir d'une clé dupliquée, et les requêtes de validation se disputent des créneaux dans une file WLM que votre ETL utilise aussi.
Pourquoi Redshift demande des précautions
Les contraintes sont des indices, pas des règles. Redshift accepte les déclarations PRIMARY KEY, UNIQUE et FOREIGN KEY et n'en applique jamais aucune. Pire : le planificateur leur fait confiance. Si vous déclarez order_id comme unique alors qu'il ne l'est pas, une requête qui s'appuie sur cette hypothèse peut renvoyer des résultats incorrects, et pas seulement s'exécuter lentement. Dans Redshift, valider l'unicité ne relève pas de l'hygiène défensive — c'est protéger la justesse de toutes les requêtes en aval.
Tout partage une même file. Redshift exécute les requêtes via la gestion de charge de travail (WLM). Un balayage de validation fait de parcours complets de tables, soumis en même temps que l'ETL nocturne, se mettra en file derrière lui ou, pire, lui prendra des créneaux. Donnez à la validation sa propre file WLM, avec une concurrence modeste et une règle de surveillance des requêtes qui interrompt tout ce qui dépasse un seuil de durée.
Des statistiques périmées changent le sens de « peu coûteux ». Après un gros COPY, les statistiques de table sont obsolètes jusqu'à l'exécution d'ANALYZE. Des plans choisis à partir de statistiques périmées peuvent transformer une agrégation rapide en jointure par diffusion. Lancez la validation après ANALYZE, pas avant.
Ces trois points mis à part, la validation sur Redshift a la même forme que partout ailleurs, ce que le guide complet de la validation des données expose de bout en bout.
Les six contrôles dont toute table a besoin
1. Complétude — valeurs nulles dans les colonnes obligatoires
SELECT COUNT(*) AS violations
FROM sales.orders
WHERE order_id IS NULL;
Redshift est orienté colonnes : cette requête ne lit donc que la colonne order_id — peu coûteux, même sur une table de faits très large. N'écrivez jamais SELECT * dans un contrôle ; vous perdriez précisément cet avantage.
2. Unicité — clés métier en double
SELECT COUNT(*) AS violations
FROM (
SELECT order_id
FROM sales.orders
WHERE order_id IS NOT NULL
GROUP BY order_id
HAVING COUNT(*) > 1
) dupes;
C'est le contrôle à exécuter en premier sur n'importe quelle table Redshift. Les doublons arrivent couramment : un COPY relancé après un échec partiel, un MERGE implémenté en delete-puis-insert dont le filtre de suppression a manqué sa cible, une extraction amont rejouée sur la même fenêtre. Comme la clé primaire déclarée ne fait rien, rien d'autre ne les attrapera.
Si la table possède une clé de tri sur la clé métier, le GROUP BY travaille sur des blocs triés et coûte nettement moins cher. Cela vaut la peine de choisir ses clés de tri en gardant cela à l'esprit.
3. Conformité — valeurs hors de l'ensemble autorisé
SELECT COUNT(*) AS violations
FROM sales.orders
WHERE status IS NOT NULL
AND status NOT IN ('pending', 'paid', 'shipped', 'refunded');
Gardez la protection contre les valeurs nulles — NULL NOT IN (...) vaut NULL, la ligne disparaît, et une colonne riche en valeurs nulles s'annonce impeccable.
Les longueurs VARCHAR de Redshift s'expriment en octets, pas en caractères. Un emoji de quatre octets dans une colonne VARCHAR(10) est tronqué au chargement, et une valeur tronquée échoue au contrôle des valeurs autorisées pour une raison qui n'a rien à voir avec le système source. Dimensionnez généreusement vos colonnes de texte.
4. Exactitude — nombres hors d'une plage plausible
SELECT COUNT(*) AS violations
FROM sales.orders
WHERE total_amount IS NOT NULL
AND (total_amount < 0 OR total_amount > 100000);
Utilisez DECIMAL/NUMERIC pour les montants. L'arithmétique DECIMAL de Redshift peut aussi déborder silencieusement vers une échelle plus large lors d'une agrégation : un contrôle de plage sur la colonne brute est donc plus fiable qu'un contrôle sur un total calculé.
5. Conformité — identifiants malformés
Redshift conserve les opérateurs d'expressions régulières de PostgreSQL :
SELECT COUNT(*) AS violations
FROM sales.orders
WHERE reference IS NOT NULL
AND reference !~ '^ORD-[0-9]{6}#39;;
~ est sensible à la casse, ~* insensible, !~ nie la correspondance. SIMILAR TO et REGEXP_COUNT/REGEXP_SUBSTR sont également disponibles. Aucune traduction de motif n'est nécessaire — contrairement à SQL Server, le motif que vous écrivez est le motif qui s'exécute.
6. Ponctualité — fraîcheur
SELECT COUNT(*) AS violations
FROM sales.orders
WHERE created_at < GETDATE() - INTERVAL '24 hours';
Le GETDATE() de Redshift renvoie de l'UTC, à l'inverse du comportement de SQL Server, et c'est une vraie source de confusion quand on porte des règles de l'un à l'autre. SYSDATE renvoie également de l'UTC. Stockez les horodatages en TIMESTAMP (Redshift ne conserve pas de fuseau) et tenez-vous-en à l'UTC par convention.
Pour un contrôle de fin de chargement qui ne touche à aucune donnée de table, SVV_TABLE_INFO expose à peu de frais des métadonnées par table.
Intégrité référentielle
SELECT COUNT(*) AS violations
FROM sales.orders o
LEFT JOIN sales.customers c ON o.customer_id = c.id
WHERE o.customer_id IS NOT NULL
AND c.id IS NULL;
Comme les clés étrangères ne sont pas appliquées, les lignes orphelines sont fréquentes après une reprise d'historique partielle. Le style de distribution détermine le coût de ce contrôle : si les deux tables ont un DISTKEY sur la colonne de jointure, la jointure est locale à chaque slice ; sinon Redshift redistribue l'un des côtés sur tout le cluster. Sur de grandes tables, c'est la différence entre quelques secondes et plusieurs minutes.
Un utilisateur en lecture seule sans risque
CREATE USER catalyst_ro PASSWORD '<generated>';
GRANT USAGE ON SCHEMA sales TO catalyst_ro;
GRANT SELECT ON ALL TABLES IN SCHEMA sales TO catalyst_ro;
ALTER DEFAULT PRIVILEGES IN SCHEMA sales
GRANT SELECT ON TABLES TO catalyst_ro;
La ligne de privilèges par défaut compte ici autant que dans Postgres : sans elle, les tables recréées par l'ETL de demain sont invisibles pour l'utilisateur de validation, et les contrôles se mettent à échouer sur des erreurs de permission qui ressemblent à des défauts de données.
Isolez ensuite la charge de travail :
-- In the WLM configuration, give the validation user group its own queue with
-- low concurrency, and a query monitoring rule that aborts on long runtime.
CREATE GROUP validators WITH USER catalyst_ro;
Du SQL ponctuel au contrat de données
Déclarer les attentes les rend relisibles, portables et indépendantes de l'outil qui charge la table. Catalyst utilise l'Open Data Contract Standard :
apiVersion: v3.0.0
kind: DataContract
info:
title: orders
version: 1.3.0
owner: data-platform
schema:
- name: orders
physicalName: orders
physicalType: table
properties:
- name: order_id
logicalType: string
physicalType: varchar(36)
required: true
primaryKey: true
quality:
- rule: nullCount
dimension: completeness
severity: error
mustBe: "0"
- rule: duplicateCount
dimension: uniqueness
severity: error
mustBe: "0"
- name: customer_id
logicalType: string
quality:
- rule: referentialIntegrity
dimension: consistency
severity: error
mustBe: "customers.id"
- name: status
logicalType: string
quality:
- rule: validValues
dimension: conformity
severity: error
mustBe: "['pending', 'paid', 'shipped', 'refunded']"
- name: total_amount
logicalType: number
physicalType: numeric(12,2)
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"
quality:
- rule: rowCount
dimension: consistency
severity: warning
mustBe: "> 0"
Catalyst importe les colonnes depuis information_schema, propose un contrat de base, compile chaque règle en SQL Redshift, et enregistre pass, warn ou fail pour chaque contrôle avec un échantillon des lignes en échec. Le YAML fait l'aller-retour avec le constructeur visuel — commentaires et ordre préservés — de sorte que le fichier relu en pull request est celui qui s'exécute.
Pièges Redshift à connaître
| Piège | Ce qui se passe | Que faire |
|---|---|---|
PRIMARY KEY non appliquée | Le planificateur lui fait confiance et renvoie de mauvais résultats | Toujours ajouter une règle duplicateCount |
FOREIGN KEY non appliquée | Orphelins après des reprises d'historique partielles | Ajouter une règle referentialIntegrity |
| File WLM partagée | Les contrôles concurrencent l'ETL | File dédiée + règle de surveillance des requêtes |
| Statistiques périmées | L'agrégation devient une jointure par diffusion | Valider après ANALYZE |
Longueurs VARCHAR en octets | Le texte multi-octets est tronqué au chargement | Dimensionner généreusement les colonnes de texte |
Pas d'ALTER DEFAULT PRIVILEGES | Les nouvelles tables sont illisibles dès demain | Accorder les privilèges par défaut sur le schéma |
GETDATE() renvoie de l'UTC | L'inverse de SQL Server | Tout conserver en UTC |
| Vues à liaison tardive | Le contrôle échoue si la table de base est supprimée | Valider les tables de base, pas les vues |
Planification et alertes
Déclenchez la validation à la fin du COPY ou du job de transformation, après ANALYZE, plutôt qu'à une heure fixe. Sur Redshift Serverless, le même conseil vaut avec une incitation supplémentaire : la capacité inactive ne coûte rien, mais un balayage de validation qui réveille le workgroup toutes les quinze minutes le maintient chaud — et facturé.
Alertez sur les transitions plutôt que sur un état persistant, et différenciez les gravités de façon à réserver error aux contrôles qui doivent réellement bloquer les consommateurs en aval. Sur Redshift, la règle d'unicité relève presque toujours du niveau error, vu ce qu'une clé primaire non appliquée fait à la justesse des requêtes.
Questions fréquentes
Redshift applique-t-il les clés primaires ?
Non. PRIMARY KEY, UNIQUE et FOREIGN KEY sont acceptées et enregistrées, mais jamais appliquées à l'écriture. Le planificateur de requêtes, lui, leur fait confiance : une clé dupliquée peut donc faire renvoyer des résultats incorrects, et pas seulement des lignes en trop. Valider l'unicité avec un contrôle explicite est le seul moyen de contrainte disponible.
Puis-je valider des données Redshift sans écrire de SQL ?
Oui. Déclarez l'attente — obligatoire, unique, valeurs autorisées, plage numérique, expression régulière, âge maximal, clé étrangère — et laissez l'outil la compiler en SQL Redshift. Catalyst importe le schéma depuis information_schema, suggère un ensemble de règles de base, et réserve le SQL écrit à la main à la logique réellement sur mesure, via une règle customSql.
Comment empêcher les requêtes de validation de ralentir mon ETL ?
Placez l'utilisateur de validation dans sa propre file WLM, avec une faible concurrence et une règle de surveillance des requêtes qui interrompt les requêtes longues, et planifiez les contrôles à la suite du chargement plutôt qu'en parallèle. Gardez les contrôles limités à une colonne — Redshift est orienté colonnes, donc une agrégation sur une seule colonne coûte radicalement moins cher que tout ce qui touche à la ligne entière.
Cela fonctionne-t-il avec Redshift Serverless et Redshift Spectrum ?
Redshift Serverless se comporte de façon identique pour tout ce que couvre ce guide ; la seule différence est la facturation, évitez donc les planifications qui maintiennent le workgroup éveillé sans nécessité. Les tables externes Spectrum se valident sans problème, mais il n'y a ni stockage local ni clé de tri à exploiter : chaque contrôle parcourt donc les objets S3 sous-jacents — limitez-les à une partition.
De quelles permissions un outil de validation a-t-il besoin ?
USAGE sur le schéma et SELECT sur les tables, plus les privilèges par défaut pour que les futures tables héritent de l'autorisation. Aucun accès en écriture n'est requis. Catalyst se connecte en lecture seule et ne stocke que des métadonnées et des résultats de contrôles.