Guide pratique · BigQuery · Snowflake · PostgreSQL · Lecture : ~25 min Teste ton niveau sur modai-lab.com/test-sql/
L'histoire vraie derriÚre ce guide Un junior à 2 500⏠par mois. Motivé, sérieux, productif.
La facture BigQuery tombe Ă la fin du mois. 7 500⏠de calcul consommĂ©. Rien que sur ses requĂȘtes.
Il ne savait pas. Personne ne lui avait appris qu'une requĂȘte mal Ă©crite a un coĂ»t rĂ©el. Personne ne lui avait dit qu'un index peut diviser ce coĂ»t par 100. En formation, les datasets font 500 lignes. En entreprise, ils en font 200 millions.
Ce guide liste les 15 requĂȘtes les plus coĂ»teuses â avec leur version optimisĂ©e et les rĂ©flexes Ă adopter immĂ©diatement.
Ce guide couvre :
Les 15 patterns qui font exploser la facture cloud
Les réflexes que personne ne t'apprend en formation
Un prompt Claude pour auditer le coĂ»t de tes requĂȘtes
đ§Ș Calibre ton niveau avant de commencer Sais-tu dĂ©jĂ expliquer pourquoi une fonction dans un WHERE peut multiplier le coĂ»t par 100 ? Fais le test en 5 minutes.
đ Faire le test sur modai-lab.com/test-sql/
_Résultat en direct · Niveau calibré · Parcours personnalisé_
BLOC 1 â Les requĂȘtes qui lisent trop de donnĂ©es RequĂȘte 1 â SELECT * sur une grande table CoĂ»t rĂ©el : sur BigQuery Ă 5$/TB, une table de 2 TB lue 30 fois par jour = 300$/jour = ~9 000$/mois.
-- â Lit TOUTES les colonnes de la table
SELECT * FROM orders WHERE statut = 'livré' ;
-- Table : 200M lignes, 50 colonnes, 2 TB â 2 TB lus Ă chaque fois
Â
-- â
Lister uniquement les colonnes nécessaires
SELECT order_id, user_id, montant, created_at, statut
FROM orders
WHERE statut = 'livré' ;
-- 4 colonnes sur 50 â ~160 GB lus â coĂ»t divisĂ© par ~12
15-requetes-sql-facture-cloud · 01 Calcul d'impact :
SELECT * â 2 TB Ă 5$/TB = 10$ par requĂȘte
SELECT 4 cols â 160 GB Ă 5$/TB = 0.80$ par requĂȘte
Â
30 exécutions/jour à 30 jours = 9 000$ vs 720$
Ăconomie : 8 280$ par mois sur une seule requĂȘte
15-requetes-sql-facture-cloud · 02 · Calcul d'impact : RĂ©flexe : ne jamais utiliser SELECT * en dehors d'une exploration ponctuelle LIMIT 100. MĂȘme pour "regarder vite".
RequĂȘte 2 â Pas de filtre sur la colonne de partition Sur les data warehouses modernes, les tables sont partitionnĂ©es (gĂ©nĂ©ralement par date). Sans filtre sur la partition, le moteur scanne tout.
-- â Scan de toutes les partitions (toute l'histoire de la table)
SELECT user_id, SUM (montant) AS ca
FROM orders
WHERE statut = 'livré' -- statut n'est pas la colonne de partition
GROUP BY user_id;
-- 5 ans de donnĂ©es lus alors qu'on veut peut-ĂȘtre juste cette annĂ©e
Â
-- â
Toujours filtrer sur la colonne de partition en premier
SELECT user_id, SUM (montant) AS ca
FROM orders
WHERE created_at >= '2024-01-01' -- filtre partition â seulement 2024
AND statut = 'livré'
GROUP BY user_id;
-- Coût réduit de 5à si la table contient 5 ans d'historique
15-requetes-sql-facture-cloud · 03 Comment savoir quelle colonne est la partition :
-- BigQuery
SELECT * FROM `projet.dataset.INFORMATION_SCHEMA.PARTITIONS`
WHERE table_name = 'orders' ;
Â
-- PostgreSQL
SELECT schemaname, tablename, partitionboundspec
FROM pg_partitions WHERE tablename = 'orders' ;
15-requetes-sql-facture-cloud · 04 · Comment savoir quelle colonne est la partition : RequĂȘte 3 â COUNT(*) suivi d'un GROUP BY sur toutes les colonnes -- â GROUP BY sur toutes les colonnes = autant de groupes que de lignes
SELECT *, COUNT (*) AS n
FROM orders
GROUP BY order_id, user_id, montant, statut, created_at, ...;
-- Résultat : une ligne par commande avec n = 1 dans tous les cas
-- Aucune valeur, lecture complĂšte de la table
Â
-- â
Identifier ce qu'on veut vraiment
-- Si on cherche les doublons sur un champ spécifique :
SELECT order_id, COUNT (*) AS n
FROM orders
GROUP BY order_id
HAVING COUNT (*) > ;
-- Lit seulement order_id â coĂ»t minimal
15-requetes-sql-facture-cloud · 05 RequĂȘte 4 â DISTINCT sur toutes les colonnes pour "nettoyer" -- â DISTINCT * force un tri complet de toute la table
SELECT DISTINCT *
FROM orders;
-- Le moteur lit tout, trie tout, compare tout
Â
-- â
Identifier pourquoi il y a des doublons et corriger la cause
-- Si doublons sur une clé spécifique :
SELECT DISTINCT order_id FROM orders; -- lecture d'une seule colonne
Â
-- Ou avec ROW_NUMBER pour dédupliquer proprement
SELECT * FROM (
SELECT *, ROW_NUMBER() OVER (PARTITION BY order_id ORDER BY created_at DESC ) AS rn
FROM orders
) t WHERE rn = ;
15-requetes-sql-facture-cloud · 06 RequĂȘte 5 â Jointure sur des tables entiĂšres sans filtres prĂ©alables -- â Jointure sur 200M lignes Ă 5M lignes sans rĂ©duire d'abord
SELECT o.order_id, p.categorie, SUM (o.montant) AS ca
FROM orders o
JOIN products p ON o.product_id = p.id
JOIN order_lines ol ON o.id = ol.order_id
WHERE o.created_at >= '2024-01-01'
AND p.categorie = 'Ălectronique'
GROUP BY o.order_id, p.categorie;
-- Le filtre est appliquĂ© APRĂS les jointures selon certains optimiseurs
Â
-- â
Réduire les tables AVANT de les joindre via CTEs
WITH commandes_recentes AS (
SELECT order_id, product_id, montant
FROM orders
WHERE created_at >= '2024-01-01' -- filtre partition : ~10M lignes au lieu de 200M
),
produits_electronique AS (
SELECT id
FROM products
WHERE categorie = 'Ălectronique' -- filtre : ~200K lignes au lieu de 5M
)
SELECT c.order_id, SUM (c.montant) AS ca
FROM commandes_recentes c
JOIN produits_electronique p ON c.product_id = p.id
GROUP BY c.order_id;
-- Jointure sur 10M Ă 200K au lieu de 200M Ă 5M â 200Ă moins de donnĂ©es
15-requetes-sql-facture-cloud · 07 đ§Ș Teste ces 5 premiers patterns sur Modai Lab Ces 5 requĂȘtes reprĂ©sentent 80% des factures cloud Ă©vitables. Modai Lab simule des tables de grande taille avec mĂ©triques de performance visibles.
đ Pratiquer sur modai-lab.com
_Exercices avec simulation de coût · Feedback instantané_
BLOC 2 â Les requĂȘtes qui font du travail inutile RequĂȘte 6 â Sous-requĂȘte corrĂ©lĂ©e sur une grande table -- â ExĂ©cutĂ©e une fois par ligne â 200M exĂ©cutions si la table a 200M lignes
SELECT
user_id,
email,
(SELECT COUNT (*) FROM orders o WHERE o.user_id = u.id) AS nb_commandes,
(SELECT SUM (montant) FROM orders o WHERE o.user_id = u.id) AS ca_total
FROM users u;
-- Chaque sous-requĂȘte = un scan partiel de orders par utilisateur
-- 10M users Ă 2 sous-requĂȘtes = 20M scans supplĂ©mentaires
Â
-- â
Agrégation en une seule passe
WITH stats AS (
SELECT user_id, COUNT (*) AS nb_commandes, SUM (montant) AS ca_total
FROM orders GROUP BY user_id
)
SELECT u.user_id, u.email,
COALESCE (s.nb_commandes, ) AS nb_commandes,
COALESCE (s.ca_total, ) AS ca_total
FROM users u
LEFT JOIN stats s ON u.id = s.user_id;
-- Une seule passe sur orders, une jointure â 20MĂ moins de travail
15-requetes-sql-facture-cloud · 08 RequĂȘte 7 â Fonction sur une colonne indexĂ©e dans le WHERE -- â La fonction empĂȘche l'utilisation de l'index â scan complet
SELECT * FROM orders WHERE YEAR(created_at) = ;
SELECT * FROM orders WHERE DATE_TRUNC('month' , created_at) = '2024-01-01' ;
SELECT * FROM orders WHERE LOWER(email_client) = 'alice@example.com' ;
SELECT * FROM orders WHERE CAST (montant AS VARCHAR ) LIKE '1%' ;
-- Dans chaque cas : l'index sur la colonne est ignoré
Â
-- â
Réécrire sans fonction sur la colonne indexée
SELECT * FROM orders
WHERE created_at >= '2024-01-01' AND created_at < '2025-01-01' ;
Â
SELECT * FROM orders
WHERE created_at >= '2024-01-01' AND created_at < '2024-02-01' ;
Â
-- Pour LOWER : créer un index fonctionnel
CREATE INDEX idx_orders_email_lower ON orders(LOWER(email_client));
SELECT * FROM orders WHERE LOWER(email_client) = 'alice@example.com' ;
-- Maintenant l'index est utilisé
15-requetes-sql-facture-cloud · 09 RequĂȘte 8 â ORDER BY sans LIMIT sur une grande table -- â Trier 200M lignes pour ne garder que les 10 premiĂšres
SELECT * FROM orders ORDER BY montant DESC ;
-- Sans LIMIT : trie TOUTES les lignes, retourne TOUTES les lignes
-- Avec LIMIT mais ORDER BY sur colonne non indexée : trie toutes les lignes avant
Â
-- â
Toujours limiter quand on explore
SELECT order_id, montant, created_at FROM orders
ORDER BY montant DESC
LIMIT ;
Â
-- â
Et créer l'index pour le tri fréquent
CREATE INDEX idx_orders_montant_desc ON orders(montant DESC );
-- Le top 100 est lu directement dans l'index sans trier toute la table
15-requetes-sql-facture-cloud · 10 RequĂȘte 9 â UNION au lieu de UNION ALL -- â UNION dĂ©duplique â tri complet du rĂ©sultat combinĂ©
SELECT user_id FROM clients_france
UNION
SELECT user_id FROM clients_belgique;
-- Le moteur lit les deux tables, combine, trie tout, élimine les doublons
Â
-- â
UNION ALL si les doublons ne posent pas de problĂšme
SELECT user_id FROM clients_france
UNION ALL
SELECT user_id FROM clients_belgique;
-- Pas de tri, pas de dĂ©duplication â 2Ă Ă 10Ă plus rapide selon le volume
Â
-- Si la déduplication est vraiment nécessaire :
SELECT DISTINCT user_id FROM (
SELECT user_id FROM clients_france
UNION ALL
SELECT user_id FROM clients_belgique
) combined;
-- MĂȘme rĂ©sultat que UNION mais avec UNION ALL d'abord
15-requetes-sql-facture-cloud · 11 RequĂȘte 10 â COUNT(DISTINCT col) sur une trĂšs grande table -- â COUNT(DISTINCT) force un tri ou un hash complet des valeurs
SELECT COUNT (DISTINCT user_id) FROM events;
-- Sur 5 milliards de lignes : trÚs lent et coûteux
Â
-- â
Pour une approximation (souvent suffisante en analytique) :
-- HyperLogLog : résultat approximatif mais 100à plus rapide
Â
-- BigQuery
SELECT APPROX_COUNT_DISTINCT(user_id) FROM events; -- erreur < 2%
Â
-- PostgreSQL (avec extension)
SELECT hll_cardinality(hll_add_agg(hll_hash_bigint(user_id))) FROM events;
Â
-- Snowflake
SELECT APPROX_COUNT_DISTINCT(user_id) FROM events;
Â
-- Si l'exact est requis : calculer une fois et matérialiser
15-requetes-sql-facture-cloud · 12 BLOC 3 â Les requĂȘtes qui se rĂ©pĂštent inutilement RequĂȘte 11 â La mĂȘme agrĂ©gation lourde rĂ©pĂ©tĂ©e plusieurs fois -- â La mĂȘme sous-requĂȘte lourde recalculĂ©e 3 fois
SELECT
mois,
ca_total,
ca_total - LAG(ca_total) OVER (ORDER BY mois) AS variation,
ca_total / (SELECT SUM (montant) FROM orders) * AS pct_total, -- recalcul 1
RANK() OVER (ORDER BY ca_total DESC ) AS rang
FROM (
SELECT DATE_TRUNC('month' , created_at) AS mois, SUM (montant) AS ca_total
FROM orders GROUP BY
) monthly
WHERE (SELECT SUM (montant) FROM orders) > ; -- recalcul 2
Â
-- â
CTE calculée une fois, réutilisée partout
WITH ca_mensuel AS (
SELECT DATE_TRUNC('month' , created_at) AS mois, SUM (montant) AS ca_total
FROM orders GROUP BY
),
total_global AS (
SELECT SUM (montant) AS total FROM orders
)
SELECT
m.mois,
m.ca_total,
m.ca_total - LAG(m.ca_total) OVER (ORDER BY m.mois) AS variation,
ROUND(. * m.ca_total / t.total, ) AS pct_total,
RANK() OVER (ORDER BY m.ca_total DESC ) AS rang
FROM ca_mensuel m
CROSS JOIN total_global t
ORDER BY m.mois;
-- orders scanné une seule fois au lieu de 3+
15-requetes-sql-facture-cloud · 13 RequĂȘte 12 â RequĂȘtes rĂ©currentes sans matĂ©rialisation -- â Ce rapport est lancĂ© 50 fois par jour par toute l'Ă©quipe
-- Ă chaque fois : scan complet de orders (2 TB)
SELECT
DATE_TRUNC('month' , o.created_at) AS mois,
p.categorie,
SUM (o.montant) AS ca,
COUNT (DISTINCT o.user_id) AS clients
FROM orders o
JOIN products p ON o.product_id = p.id
WHERE o.statut = 'livré'
GROUP BY , ;
-- 50 exécutions à 2 TB = 100 TB lus par jour = 500$/jour
Â
-- â
Matérialiser une fois par jour (dbt, Airflow, ou job planifié)
-- Le job de nuit calcule et stocke le résultat :
CREATE TABLE rapport_ca_cache AS
SELECT
DATE_TRUNC('month' , o.created_at) AS mois,
p.categorie,
SUM (o.montant) AS ca,
COUNT (DISTINCT o.user_id) AS clients
FROM orders o
JOIN products p ON o.product_id = p.id
WHERE o.statut = 'livré'
GROUP BY , ;
Â
-- Les 50 requĂȘtes quotidiennes lisent la cache (quelques Ko) :
SELECT * FROM rapport_ca_cache ORDER BY mois DESC , ca DESC ;
-- Coût : quasi nul au lieu de 500$/jour
15-requetes-sql-facture-cloud · 14 RequĂȘte 13 â Les mĂȘmes requĂȘtes relancĂ©es sans utiliser le cache Sur BigQuery et Snowflake, les rĂ©sultats sont mis en cache. Une requĂȘte identique relancĂ©e est gratuite . Une lĂ©gĂšre variation invalide le cache.
-- â Ces deux requĂȘtes semblent identiques mais ne partagent pas le cache
SELECT user_id, SUM (montant) FROM orders GROUP BY user_id;
SELECT user_id, SUM (montant) FROM orders GROUP BY ; -- syntaxe diffĂ©rente â nouveau scan
Â
-- â La date courante invalide le cache Ă chaque seconde
SELECT COUNT (*) FROM orders WHERE created_at >= CURRENT_DATE;
-- CURRENT_DATE change â jamais de cache
Â
-- â
Standardiser la syntaxe des requĂȘtes rĂ©currentes
-- Toujours GROUP BY colonne_nom, jamais GROUP BY numéro
-- ParamĂ©trer les dates pour les requĂȘtes de veille
SELECT COUNT (*) FROM orders WHERE created_at >= '2024-11-03' ; -- date fixe = cache
Â
-- â
Ou interroger la table matérialisée mise à jour chaque nuit
SELECT nb_commandes_du_jour FROM stats_cache WHERE jour = CURRENT_DATE - ;
15-requetes-sql-facture-cloud · 15 đ§Ș Identifie les requĂȘtes redondantes dans tes scripts Sous-requĂȘtes rĂ©pĂ©tĂ©es, rapports non matĂ©rialisĂ©s, cache invalidĂ© : ce sont les patterns les plus difficiles Ă voir â et les plus coĂ»teux.
đ Tester sur modai-lab.com/test-sql/
_Score immédiat · Simulation de coût · Feedback senior_
BLOC 4 â Les requĂȘtes qui ignorent la structure des donnĂ©es RequĂȘte 14 â JOIN sur un champ VARCHAR non normalisĂ© -- â Les espaces cachĂ©s et la casse diffĂ©rente empĂȘchent les correspondances
SELECT o.order_id, u.nom
FROM orders o
JOIN users u ON o.email_client = u.email;
-- 'Alice@EXAMPLE.COM ' â 'alice@example.com'
-- Résultat : des milliers de commandes orphelines silencieusement
Â
-- SymptĂŽme : COUNT avant JOIN >> COUNT aprĂšs JOIN
SELECT
(SELECT COUNT (*) FROM orders) AS total_orders,
COUNT (*) AS orders_apres_join
FROM orders o JOIN users u ON o.email_client = u.email;
Â
-- â
Normaliser les deux cÎtés
SELECT o.order_id, u.nom
FROM orders o
JOIN users u ON LOWER(TRIM(o.email_client)) = LOWER(TRIM(u.email));
Â
-- Encore mieux : normaliser Ă l'import (migration)
UPDATE orders SET email_client = LOWER(TRIM(email_client));
UPDATE users SET email = LOWER(TRIM(email));
-- Index aprĂšs normalisation
CREATE INDEX idx_users_email ON users(email);
15-requetes-sql-facture-cloud · 16 -- â Fan-out : chaque ligne de order_lines multiplie les lignes d'orders
-- Si une commande a 5 produits â elle apparaĂźt 5 fois â SUM Ă 5
SELECT
u.id,
u.nom,
SUM (o.montant) AS ca_total -- multiplié par le nombre de lignes par commande
FROM users u
JOIN orders o ON u.id = o.user_id
JOIN order_lines ol ON o.id = ol.order_id -- â fan-out ici
GROUP BY u.id, u.nom;
Â
-- Diagnostic du fan-out
SELECT
COUNT (*) AS lignes_totales,
COUNT (DISTINCT o.id) AS commandes_distinctes,
ROUND(COUNT (*) * . / NULLIF(COUNT (DISTINCT o.id), ), ) AS ratio_fan_out
FROM orders o
JOIN order_lines ol ON o.id = ol.order_id;
-- ratio > 1 = problĂšme â chaque commande est comptĂ©e plusieurs fois
Â
-- â
Agréger order_lines avant de joindre
WITH ca_par_commande AS (
SELECT order_id, SUM (prix_unitaire * quantite) AS montant_lignes
FROM order_lines GROUP BY order_id
)
SELECT u.id, u.nom, SUM (c.montant_lignes) AS ca_total
FROM users u
JOIN orders o ON u.id = o.user_id
JOIN ca_par_commande c ON o.id = c.order_id
GROUP BY u.id, u.nom;
-- Chaque commande apparaĂźt exactement une fois
15-requetes-sql-facture-cloud · 17 đ§Ș Pratique la dĂ©tection de fan-out et de VARCHAR non normalisĂ© Ces deux patterns font partie des exercices intermĂ©diaires de Modai Lab. Tu pars d'une requĂȘte avec un bug silencieux â Ă toi de le trouver et corriger.
đ Pratiquer sur modai-lab.com
_Scénarios enterprise · Feedback senior · Progression mesurée_
RĂ©flexe 1 â Estimer le coĂ»t AVANT d'exĂ©cuter BigQuery : bouton "Query Preview" â affiche les bytes estimĂ©s
Ne pas lancer si > 500 MB pour une requĂȘte de dĂ©veloppement
Â
PostgreSQL : EXPLAIN sans ANALYZE
â Lit le plan sans exĂ©cuter la requĂȘte
Â
Snowflake : EXPLAIN USING TABULAR
â Affiche le plan et les statistiques estimĂ©es
15-requetes-sql-facture-cloud · 18 RĂ©flexe 2 â DĂ©velopper sur un sous-ensemble de donnĂ©es -- Pendant la phase de dĂ©veloppement
WITH sample AS (
SELECT * FROM orders
WHERE created_at >= '2024-11-01' -- 1 mois seulement
LIMIT
)
-- Tester toute la logique sur le sample
SELECT ... FROM sample ...;
Â
-- Une fois la logique validée : retirer les restrictions et lancer en prod
15-requetes-sql-facture-cloud · 19 RĂ©flexe 3 â VĂ©rifier le fan-out aprĂšs chaque JOIN -- Template Ă appliquer systĂ©matiquement aprĂšs toute jointure
SELECT
COUNT (*) AS lignes_totales,
COUNT (DISTINCT table_principale.id) AS entites_distinctes,
ROUND(COUNT (*) * . / NULLIF(COUNT (DISTINCT table_principale.id), ), ) AS ratio
FROM table_principale
JOIN table_secondaire ON ...;
-- ratio = 1 â OK
-- ratio > 1 â fan-out â agrĂ©ger avant de joindre
15-requetes-sql-facture-cloud · 20 RĂ©flexe 4 â MatĂ©rialiser les rapports partagĂ©s Rapport vu 50 fois par jour par l'Ă©quipe ?
â Ne pas le laisser scanner la table brute Ă chaque fois
â CrĂ©er une table matĂ©rialisĂ©e mise Ă jour 1Ă par jour (ou par heure si besoin)
â Le rapport lit la table matĂ©rialisĂ©e : quelques Ko au lieu de plusieurs TB
15-requetes-sql-facture-cloud · 21 RĂ©flexe 5 â Utiliser APPROX_COUNT_DISTINCT pour les mĂ©triques approximatives -- En analytique, une approximation Ă 2% est souvent suffisante
-- Et 100Ă moins chĂšre qu'un COUNT DISTINCT exact sur 5 milliards de lignes
Â
SELECT APPROX_COUNT_DISTINCT(user_id) AS utilisateurs_uniques FROM events;
-- BigQuery, Snowflake : disponible nativement
-- Si l'exact est requis : prĂ©ciser explicitement dans le commentaire de la requĂȘte
15-requetes-sql-facture-cloud · 22 RĂ©flexe 6 â Documenter les requĂȘtes coĂ»teuses avec leur coĂ»t estimĂ© -- Bonne pratique : commenter le coĂ»t estimĂ© et la frĂ©quence
-- Coût estimé : ~50 GB lus, ~0.25$ par exécution
-- Fréquence : 1à par nuit dans le job ETL 23h00
-- DerniÚre révision : 2024-11-03 - Optimisé le SELECT * initial
SELECT
user_id,
DATE_TRUNC('month' , created_at) AS mois,
SUM (montant) AS ca
FROM orders
WHERE created_at >= '2024-01-01' -- filtre partition : réduit de 2 TB à 160 GB
GROUP BY , ;
15-requetes-sql-facture-cloud · 23 PARTIE 6 â Prompt Claude pour auditer le coĂ»t de tes requĂȘtes Prompt d'audit coĂ»t complet Tu es un expert SQL performance et coĂ»t cloud. Audite la requĂȘte suivante
pour identifier les 15 patterns de gaspillage les plus courants.
Â
REQUĂTE Ă AUDITER :
[coller ta requĂȘte]
Â
CONTEXTE :
- Cloud/SGBD : [BigQuery / Snowflake / Redshift / PostgreSQL]
- Table principale : [nom, volume approximatif, partitionnement]
- Fréquence d'exécution : [ex: 50 fois par jour par l'équipe]
- Colonnes réellement utilisées dans le résultat final : [lister]
Â
Vérifie dans l'ordre :
Â
1. SELECT * â lister les colonnes rĂ©ellement nĂ©cessaires
2. Filtre de partition manquant â identifier la colonne de partition
3. Jointures sur tables entiĂšres â proposer des CTEs filtrĂ©es
4. Sous-requĂȘtes corrĂ©lĂ©es â remplacer par JOIN ou window function
5. Fonctions sur colonnes indexĂ©es dans WHERE â réécrire sans fonction
6. ORDER BY sans LIMIT â limiter le rĂ©sultat
7. UNION vs UNION ALL â UNION ALL si doublons acceptables
8. COUNT(DISTINCT) sur grande table â APPROX_COUNT_DISTINCT si applicable
9. AgrĂ©gation rĂ©pĂ©tĂ©e â CTE calculĂ©e une fois
10. Fan-out de JOIN â agrĂ©ger avant de joindre
11. VARCHAR non normalisĂ© dans la clĂ© de JOIN â normaliser
12. Rapport rĂ©current non matĂ©rialisĂ© â proposer une table matĂ©rialisĂ©e
13. RequĂȘte sans filtre de date â ajouter un filtre temporel par dĂ©faut
14. DISTINCT * â identifier et corriger la cause des doublons
15. Cache invalidĂ© par date courante â paramĂ©trer
Â
Pour chaque problÚme trouvé :
- Expliquer l'impact en volume de données
- Donner la version corrigée avec commentaires
- Estimer le gain (en % de données lues ou en $)
Â
Format : une section par problĂšme trouvĂ©, â / â
. Version finale optimisée à la fin.
15-requetes-sql-facture-cloud · 24 Prompt rapide Audite cette requĂȘte SQL pour les 5 patterns les plus coĂ»teux :
SELECT *, partition non filtrée, jointures sans filtre préalable,
sous-requĂȘtes corrĂ©lĂ©es, fonctions sur colonnes indexĂ©es.
Â
Cloud : [BigQuery / Snowflake / PostgreSQL]
Volume : [taille approximative des tables]
Â
Donne la version optimisée avec l'estimation du gain en %.
Â
[coller ta requĂȘte]
15-requetes-sql-facture-cloud · 25 RĂ©capitulatif â Les 15 requĂȘtes et leur coĂ»t Bloc 1 â Trop de donnĂ©es lues :
#
Erreur
Impact
Correction
1
SELECT *
Lit 10-50à plus de données
Lister les colonnes
2
Pas de filtre de partition
Scan complet de l'historique
WHERE colonne_partition >= date
3
GROUP BY sur toutes les colonnes
Scan inutile
Identifier la vraie agrégation
4
DISTINCT *
Tri de la table entiĂšre
Corriger la cause des doublons
5
JOIN sans filtres préalables
Jointure sur volumes maximaux
CTEs filtrées avant JOIN
Bloc 2 â Travail inutile :
#
Erreur
Impact
Correction
6
Sous-requĂȘte corrĂ©lĂ©e
N exécutions pour N lignes
CTE + JOIN
7
Fonction sur colonne indexée
Index ignorĂ© â scan complet
Réécrire sans fonction
8
ORDER BY sans LIMIT
Tri de 200M lignes
LIMIT + index de tri
9
UNION au lieu de UNION ALL
Tri et déduplication inutiles
UNION ALL
10
COUNT(DISTINCT) sur 5B lignes
Hash de tout le volume
APPROX_COUNT_DISTINCT
Bloc 3 â RĂ©pĂ©titions inutiles :
#
Erreur
Impact
Correction
11
AgrĂ©gation rĂ©pĂ©tĂ©e en sous-requĂȘte
N scans au lieu de 1
CTE calculée une fois
12
Rapport récurrent non matérialisé
Coût à fréquence chaque jour
Table matérialisée
13
Cache invalidé par date courante
Jamais de cache
Date fixe, table matérialisée
Bloc 4 â Structure ignorĂ©e :
#
Erreur
Impact
Correction
14
JOIN sur VARCHAR non normalisé
Lignes perdues silencieusement
LOWER(TRIM(...))
15
Fan-out de JOIN
Métriques multipliées
Agréger avant de joindre
â
Checklist avant d'exĂ©cuter une requĂȘte en prod â Pas de SELECT * â colonnes listĂ©es explicitement
â Filtre sur la colonne de partition prĂ©sent
â CoĂ»t estimĂ© avant exĂ©cution (dry run, EXPLAIN)
â Pas de sous-requĂȘte corrĂ©lĂ©e â CTE ou window function
â Pas de fonction sur une colonne indexĂ©e dans le WHERE
â ORDER BY avec LIMIT si on n'a pas besoin de tout le rĂ©sultat
â UNION ALL sauf si la dĂ©duplication est explicitement nĂ©cessaire
â Fan-out vĂ©rifiĂ© aprĂšs chaque JOIN (COUNT(*) / COUNT(DISTINCT id) = 1)
â ClĂ©s de JOIN normalisĂ©es (pas d'espaces, pas de casse mixte)
â Si requĂȘte relancĂ©e par toute l'Ă©quipe â matĂ©rialiser le rĂ©sultat
đ Pratiquer sur Modai Lab Ces 15 patterns ne se retiennent pas en lisant une liste. Ils s'ancrent en les dĂ©tectant dans de vraies requĂȘtes, sur de vraies donnĂ©es, avec du feedback immĂ©diat.
đ modai-lab.com/test-sql/ â Exercices sur grandes tables simulĂ©es · MĂ©triques de coĂ»t · Feedback senior
_Guide offert suite au post LinkedIn · EntraĂźnez-vous sur modai-lab.com/test-sql/ · Partagez librement â»ïž_