Une étude ErrorLLM a constaté que seulement 3 % des requêtes SQL incorrectes génèrent des avertissements à l’exécution. Les 97 % restants s’exécutent avec succès et retournent des résultats incorrects. Le SQL généré par l’IA qui compile et s’exécute n’est donc pas une preuve de correction — c’est le mode d’échec principal.
Le problème du compile-mais-faux
L’hypothèse traditionnelle en développement logiciel est que les erreurs se manifestent comme des échecs — exceptions, plantages, échecs de tests. En SQL, cette hypothèse est catastrophiquement fausse. Une requête avec une condition JOIN manquante n’échoue pas ; elle produit un produit cartésien et gonfle les comptages de lignes. Une requête avec des filtres temporels incohérents n’échoue pas ; elle compare des pommes et des oranges sur différentes périodes de temps. Une requête qui agrège au mauvais grain n’échoue pas ; elle rapporte des chiffres vraisemblables qui se trouvent être incorrects.
Les outils IA aggravent cela pour une raison spécifique : ils sont optimisés pour produire du code qui compile et s’exécute. Un LLM entraîné sur du code a un fort a priori vers des sorties syntaxiquement valides. Il génère rarement une requête qui échoue à l’analyse. Il génère régulièrement une requête qui s’analyse, s’exécute et retourne un jeu de résultats qui ne reflète pas ce que vous avez demandé.
Exemples réels
Thomson Reuters Labs a documenté cela en production. Ils ont trouvé que 73 % des analyses SQL temporelles générées par l’IA avaient des filtres temporels incohérents. Chacune de ces requêtes s’est exécutée sans erreur.
Le pattern d’échec spécifique : un agent IA a généré du SQL de taux de conversion qui a gonflé les résultats d’environ 40 % parce que les filtres temporels étaient appliqués de manière incohérente sur des tables jointes. Une table était filtrée sur le mois courant. La table jointe n’était pas filtrée du tout, ou était filtrée sur une plage de temps différente. Le taux de conversion résultant était mathématiquement valide mais sémantiquement sans signification.
78 % des erreurs SQL dans leur analyse se sont produites quand l’agent a joint des tables sans identifier correctement les relations de clé primaire. Le cas classique : jointure sur une clé non unique qui produit un fan-out silencieux. Si orders a une ligne par commande mais que order_items a plusieurs lignes par commande, les joindre sans agréger d’abord multiplie silencieusement vos chiffres de revenu. La requête s’exécute. Les chiffres semblent raisonnables. Ils sont juste incorrects.
Un praticien chez Recce a documenté un pattern similaire avec Claude Code. L’agent a construit des modèles dbt de zéro sur Snowflake — sources, modèles de base, modèles intermédiaires et marts. Il a suivi les conventions de nommage, utilisé correctement les CTEs, et a même rendu certains modèles intermédiaires incrémentaux sans qu’on le lui demande. Mais il a aussi silencieusement filtré les lignes avec des valeurs org_id manquantes, prenant une décision de qualité des données qui aurait dû être signalée à un humain. La sortie était propre, bien structurée, et discrètement incomplète.
Pourquoi c’est structurellement difficile à corriger
Le problème du compile-mais-faux n’est pas un bug dans un outil IA particulier. C’est une conséquence du fonctionnement de SQL. SQL est déclaratif — vous décrivez ce que vous voulez, et le moteur trouve comment l’obtenir. Il n’y a pas de système de types qui détecte « vous avez joint sur le mauvais grain ». Il n’y a pas d’avertissement du compilateur pour « vos filtres temporels sont incohérents ». Le langage lui-même ne distingue pas entre un résultat correct et un résultat vraisemblable mais incorrect.
Les outils IA aggravent cela parce qu’ils manquent du contexte métier pour savoir ce que « correct » signifie. Un modèle entraîné sur des millions d’exemples SQL peut générer du code parfaitement idiomatique qui mal comprend vos règles métier spécifiques. Il ne sait pas que votre table orders inclut les commandes annulées qui doivent être exclues des calculs de revenu. Il ne sait pas que votre event_timestamp est en UTC mais que vos rapports d’activité sont en heure de Paris. Il ne sait pas que joindre users à sessions devrait être un LEFT JOIN pour préserver les utilisateurs sans sessions, pas un INNER JOIN qui les supprime.
Ce qui détecte ces erreurs
Les linters et formateurs ne détectent rien ici. Le SQL est bien formé. Les erreurs sont sémantiques, pas syntaxiques.
Pratiques de revue
La défense la plus fiable est la revue humaine focalisée sur les domaines spécifiques où le SQL généré par l’IA échoue. Lors de la revue du code IA, vérifiez dans cet ordre :
- Conditions JOIN : la jointure se fait-elle sur la bonne clé ? La clé est-elle unique au moins d’un côté ? Le type de jointure est-il approprié (LEFT vs INNER) ?
- Filtres temporels : les filtres date/heure sont-ils appliqués de manière cohérente sur toutes les tables jointes ? Utilisent-ils la même granularité ?
- Gestion des NULL : la requête gère-t-elle les NULL dans les colonnes de filtre, de jointure et d’agrégation ?
SUM(revenue)ignore les NULL ;AVG(revenue)ignore les NULL mais change le dénominateur ;COUNT(revenue)ne compte que les non-NULL tandis queCOUNT(*)compte tout. - Grain d’agrégation : le GROUP BY correspond-il au grain de sortie prévu ? Toutes les colonnes non agrégées sont-elles dans le GROUP BY ?
- Complétude des filtres : y a-t-il des filtres que l’IA aurait dû appliquer mais n’a pas appliqués ? (Exclusion des données de test, commandes annulées, utilisateurs internes, etc.)
Tests
La taxonomie des tests dbt fournit la réponse structurelle. Les tests génériques détectent certaines erreurs silencieuses — un test unique sur une clé primaire détectera un fan-out accidentel dû à une mauvaise jointure. Les tests relationships détectent les clés étrangères orphelines. Mais la plupart des erreurs sémantiques nécessitent soit des tests singuliers (assertions SQL personnalisées), soit des tests unitaires (entrées mockées avec des sorties attendues).
Pour le problème des filtres temporels en particulier, les tests unitaires sont la défense la plus efficace :
unit_tests: - name: test_conversion_rate_temporal_consistency model: mrt__marketing__conversion_rates given: - input: ref('int__sessions') rows: - {session_id: 1, session_date: "2026-01-15", converted: true} - {session_id: 2, session_date: "2026-02-15", converted: false} - input: ref('int__orders') rows: - {order_id: 1, order_date: "2026-01-15", session_id: 1} expect: rows: - {month: "2026-01", sessions: 1, conversions: 1, conversion_rate: 1.0} - {month: "2026-02", sessions: 1, conversions: 0, conversion_rate: 0.0}Si le modèle applique des filtres temporels de manière incohérente, la sortie attendue ne correspond pas. Le test unitaire détecte exactement le mode d’échec documenté par Thomson Reuters.
Détection d’anomalies
Elementary pour dbt détecte la classe d’erreurs silencieuses qui se manifestent comme des glissements de distribution plutôt que des valeurs franchement incorrectes. Une requête qui gonfle les taux de conversion de 40 % déclenchera un test column_anomalies si la métrique a une base historique. Les anomalies de volume détectent le cas inverse — une requête qui supprime silencieusement des lignes produit moins de lignes en sortie que prévu.
Implications de revue
Le taux d’avertissement de 3 % signifie que les contrôles de qualité standard (« ça compile ? » et « ça s’exécute ? ») ne détectent pas la plupart des erreurs SQL générées par l’IA. Un développeur humain qui écrit une mauvaise jointure peut expliquer sa logique de jointure ; le code IA ne fournit aucun signal sur la confiance dans les décisions de jointure.
Revoyez le SQL généré par l’IA en portant attention à : les conditions JOIN, la cohérence des filtres temporels, la gestion des NULL, le grain d’agrégation et la complétude des filtres — les domaines où les erreurs sémantiques se concentrent mais qu’aucun linter ne détecte.