ServicesÀ proposNotesContact Me contacter →
EN FR
Note

Le paramètre row_condition dans dbt-expectations

Comment le paramètre row_condition de dbt-expectations permet de filtrer les tests sur des segments spécifiques — sans SQL personnalisé.

Planté
dbtdata qualitytesting

Presque tous les tests du package dbt-expectations supportent un paramètre row_condition — une clause WHERE SQL qui filtre les lignes auxquelles le test s’applique. Ce paramètre élimine le besoin de nombreux tests singuliers personnalisés en permettant un filtrage conditionnel directement en YAML.

Le problème qu’il résout

Les données réelles ne sont pas uniformes. Une vérification not_null sur account_id est pertinente pour les abonnements actifs, mais pas pour les abonnements annulés. Une regex sur les emails est utile quand les emails existent, mais produit des faux positifs sur les champs nullables. Une plage de valeurs pour order_value diffère selon les pays ou les types de commande.

Sans row_condition, trois mauvaises options s’offrent à vous :

  1. Ignorer le test parce qu’il ne s’applique pas à toutes les lignes. Vous perdez la couverture sur les lignes où il s’applique réellement.
  2. Écrire un test singulier personnalisé avec une clause WHERE codée en dur. Cela fonctionne, mais ne passe pas à l’échelle — vous finissez avec un répertoire tests/ rempli de fichiers SQL à usage unique.
  3. Restructurer votre modèle pour séparer les segments en tables distinctes ou ajouter des colonnes de filtrage. Cela modifie votre modèle de données pour accommoder les tests, ce qui est à l’envers.

row_condition offre une quatrième option : appliquer n’importe quel test pré-construit à un segment spécifique des données, de façon déclarative en YAML.

Patterns essentiels

Validation de champs nullables

Le cas d’usage le plus courant. Vous souhaitez valider le format d’une colonne, mais les valeurs NULL sont acceptables (le champ est optionnel). Sans row_condition, les valeurs NULL sont testées contre la regex et échouent :

columns:
- name: email
tests:
- dbt_expectations.expect_column_values_to_match_regex:
regex: '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$'
row_condition: "email is not null"

Cela signifie : « tout email existant doit être correctement formaté, mais il est acceptable que l’email soit NULL. » Sans row_condition, vous devriez choisir entre ignorer le test ou accepter des faux positifs.

Filtrage par statut

Les règles métier dépendent souvent de l’état d’un enregistrement. Un abonnement actif doit avoir un compte. Une commande expédiée doit avoir un numéro de suivi. Un paiement complété doit avoir un montant non nul :

columns:
- name: account_id
tests:
- dbt_expectations.expect_column_values_to_not_be_null:
row_condition: "subscription_status = 'active'"
- name: tracking_number
tests:
- dbt_expectations.expect_column_values_to_not_be_null:
row_condition: "shipping_status = 'shipped'"
- name: payment_amount
tests:
- dbt_expectations.expect_column_values_to_be_between:
min_value: 0
max_value: 1000000
row_condition: "payment_status = 'completed'"

Chaque test applique une règle qui n’a de sens que pour un sous-ensemble d’enregistrements. Le YAML est lisible, déclaratif, et ne nécessite aucun SQL personnalisé.

Plages spécifiques par segment

Différents segments de vos données peuvent avoir des plages valides différentes. Les valeurs de commande en France diffèrent de celles aux États-Unis. Les transactions B2B diffèrent du B2C. Les métriques du niveau premium diffèrent du niveau gratuit :

columns:
- name: order_value
tests:
- dbt_expectations.expect_column_values_to_be_between:
min_value: 0
max_value: 50000
row_condition: "country_code = 'FR' and order_status = 'completed'"
- dbt_expectations.expect_column_values_to_be_between:
min_value: 0
max_value: 100000
row_condition: "country_code = 'US' and order_status = 'completed'"

Vous pouvez empiler plusieurs tests sur la même colonne avec des valeurs row_condition différentes. Chaque test s’évalue indépendamment. Vous obtenez ainsi une validation spécifique par segment sans avoir à diviser votre modèle en tables séparées.

Filtrage par partition pour BigQuery

Sur BigQuery, row_condition sert également d’outil d’optimisation des performances. Si votre table est partitionnée par date, filtrer sur la colonne de partition évite les scans complets de la table :

columns:
- name: order_value
tests:
- dbt_expectations.expect_column_values_to_be_between:
min_value: 0
max_value: 1000000
row_condition: "event_date >= current_date() - 30"

Sans ce filtre, le test scanne toute la table. Avec, BigQuery limite le scan aux 30 derniers jours de partitions. Sur une table avec des années d’historique, cela peut réduire le coût du test de 90 %+.

Ce pattern est particulièrement important pour les tests les plus coûteux comme expect_column_mean_to_be_between et expect_row_values_to_have_data_for_every_n_datepart, où les scans complets sur des jeux de données volumineux peuvent être genuinement onéreux.

Fonctionnement interne

Le paramètre row_condition se compile en clause WHERE dans le SQL généré. Quand vous écrivez :

- dbt_expectations.expect_column_values_to_be_between:
min_value: 0
max_value: 100
row_condition: "status = 'active'"

Le SQL du test compilé ressemble à :

SELECT COUNT(*)
FROM your_model
WHERE status = 'active'
AND (column_value < 0 OR column_value > 100)

row_condition supporte donc toute expression SQL valide que votre entrepôt comprend : logique AND/OR, listes IN, sous-requêtes (bien que les sous-requêtes dans les conditions de test puissent être fragiles), fonctions de date et vérifications NULL.

Quand ne pas l’utiliser

row_condition n’est pas un substitut à une modélisation des données correcte. Si vous vous retrouvez à écrire le même row_condition complexe pour dix tests, c’est le signe qu’il faut créer un modèle intermédiaire filtré ou une vue qui applique le filtre en amont. Le YAML de test doit exprimer ce que vous validez, et non reproduire de la logique de transformation.

De même, évitez les expressions row_condition si complexes qu’elles deviennent elles-mêmes une source de bugs. Si la condition s’étend sur plusieurs lignes ou utilise une logique imbriquée, un test singulier (un fichier SQL autonome) est plus maintenable. Vous bénéficiez de la coloration syntaxique, des diffs en contrôle de version, et de la possibilité d’exécuter la requête directement pour le débogage.

Le point d’équilibre se situe dans des conditions de 1-2 prédicats : "status = 'active'", "email is not null", "country_code = 'FR' and order_date >= '2024-01-01'". Ces conditions sont lisibles d’un coup d’œil et peu susceptibles de contenir des bugs subtils.