Voir le code
# Installation de DuckDB
!pip install duckdb --quiet
print("✅ DuckDB installé !")clients ?WHERE et HAVING ?WITH) ?Ce module couvre les fondamentaux et concepts avancés de SQL, avec une pratique directe via Python + DuckDB.
┌─────────────────────────────────────────────────────────────────┐
│ FOCUS DU COURS │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Ce cours utilise Python + DuckDB pour apprendre SQL │
│ │
│ ✅ SQL standard (compatible PostgreSQL, MySQL, etc.) │
│ ✅ Exécution directe dans Jupyter (pas de serveur) │
│ ✅ Intégration native avec Pandas │
│ ✅ Compétences transférables à tout SGBD │
│ │
└─────────────────────────────────────────────────────────────────┘
💡 Le SQL appris ici fonctionne sur PostgreSQL, MySQL, SQLite, BigQuery, Snowflake, etc. avec des variations mineures de syntaxe.
| Niveau | Compétence |
|---|---|
| ✅ Requis | Avoir suivi le module 06_intro_relational_databases |
| ✅ Requis | Comprendre les concepts de tables, colonnes, clés |
| ✅ Requis | Bases de Python (module 04 et 05) |
À la fin de ce notebook, tu seras capable de :
SELECT, WHERE, ORDER BYCOUNT, SUM, AVG)GROUP BY et HAVINGJOIN, LEFT JOIN)DATE_TRUNC, EXTRACT)CASE, les CTEs et les Window Functions💡 Ce notebook est interactif : tu peux exécuter les cellules de code directement !
SQL (Structured Query Language) est un langage de requêtes pour interagir avec des bases de données relationnelles.
| Action | Commande SQL |
|---|---|
| 🔍 Rechercher | SELECT |
| ✏️ Insérer | INSERT |
| 🔄 Modifier | UPDATE |
| 🗑️ Supprimer | DELETE |
| 📊 Agréger | COUNT, SUM, AVG |
| 🔗 Joindre | JOIN |
Avant de plonger dans Python + DuckDB, sache qu’il existe de nombreux outils en ligne gratuits pour tester des requêtes SQL sur différents SGBD. C’est utile pour :
| Outil | URL | SGBD supportés | Points forts |
|---|---|---|---|
| DB Fiddle | db-fiddle.com | PostgreSQL, MySQL, SQLite | Interface claire, partage facile |
| SQL Fiddle | sqlfiddle.com | MySQL, PostgreSQL, Oracle, SQL Server | Le classique, multi-SGBD |
| SQLite Online | sqliteonline.com | SQLite, PostgreSQL, MySQL | Très simple, import CSV |
| Programiz SQL | programiz.com/sql/online-compiler | SQLite | Idéal débutants, tutoriels intégrés |
| OneCompiler | onecompiler.com/mysql | MySQL, PostgreSQL, SQL Server | Rapide, moderne |
| Replit | replit.com | SQLite, PostgreSQL | Environnement complet, collaboratif |
Le SQL est standardisé, mais chaque SGBD a ses particularités :
| Fonctionnalité | PostgreSQL | MySQL | SQL Server | SQLite |
|---|---|---|---|---|
| Concaténation | \|\| ou CONCAT() |
CONCAT() |
+ ou CONCAT() |
\|\| |
| Limite résultats | LIMIT 10 |
LIMIT 10 |
TOP 10 |
LIMIT 10 |
| Auto-increment | SERIAL |
AUTO_INCREMENT |
IDENTITY |
AUTOINCREMENT |
| Date actuelle | CURRENT_DATE |
CURDATE() |
GETDATE() |
DATE('now') |
| Booléens | TRUE/FALSE |
1/0 |
1/0 |
1/0 |
💡 DuckDB utilise une syntaxe proche de PostgreSQL, ce qui est idéal car PostgreSQL est le standard de facto en Data Engineering.
| Avantage | Explication |
|---|---|
| Tout-en-un | SQL + Python dans le même notebook |
| Pas d’installation serveur | DuckDB tourne en mémoire |
| Intégration Pandas | Résultats directement en DataFrame |
| Fichiers directs | Lire CSV/Parquet/JSON sans import |
| Syntaxe standard | Compatible PostgreSQL → transférable |
| Performance | Optimisé pour l’analytics (OLAP) |
Les compétences SQL apprises ici sont directement applicables à PostgreSQL, BigQuery, Snowflake, Redshift, et tout autre SGBD en production.
DuckDB est une base de données analytique embarquée (comme SQLite, mais optimisée pour l’analytics).
┌─────────────────────────────────────────────────────────────┐
│ POURQUOI DUCKDB ? │
├─────────────────────────────────────────────────────────────┤
│ │
│ ✅ Zéro installation serveur (fonctionne en mémoire) │
│ ✅ Syntaxe SQL standard (PostgreSQL-like) │
│ ✅ Intégration native avec Pandas │
│ ✅ Très rapide pour l'analytics (colonnar storage) │
│ ✅ Lit directement CSV, Parquet, JSON │
│ ✅ Parfait pour apprendre SQL dans un notebook │
│ │
└─────────────────────────────────────────────────────────────┘
| Critère | SQLite | DuckDB | PostgreSQL |
|---|---|---|---|
| Installation | Embarqué | Embarqué | Serveur |
| Optimisé pour | OLTP (transactions) | OLAP (analytics) | Les deux |
| Intégration Pandas | ⚠️ Via SQLAlchemy | ✅ Native | ⚠️ Via psycopg2 |
| Fichiers CSV/Parquet | ❌ Non | ✅ Direct | ❌ Non |
| Usage | Mobile, embarqué | Data Science, ETL | Production |
# Création des tables de démonstration
# Table clients
con.execute("""
CREATE TABLE clients (
id_client INTEGER PRIMARY KEY,
nom VARCHAR(50),
email VARCHAR(100),
pays VARCHAR(50),
date_inscription DATE
);
""")
con.execute("""
INSERT INTO clients VALUES
(1, 'Alice', 'alice@mail.com', 'France', '2023-01-15'),
(2, 'Bob', 'bob@mail.com', 'France', '2023-02-20'),
(3, 'Charlie', 'charlie@mail.com', 'Allemagne', '2023-03-10'),
(4, 'Diana', 'diana@mail.com', 'Belgique', '2023-04-05'),
(5, 'Eve', 'eve@mail.com', 'France', '2023-05-12');
""")
# Table commandes
con.execute("""
CREATE TABLE commandes (
id_commande INTEGER PRIMARY KEY,
id_client INTEGER,
produit VARCHAR(50),
montant DECIMAL(10,2),
date_commande DATE,
FOREIGN KEY (id_client) REFERENCES clients(id_client)
);
""")
con.execute("""
INSERT INTO commandes VALUES
(1, 1, 'Clavier', 50.00, '2023-07-12'),
(2, 1, 'Souris', 25.00, '2023-07-15'),
(3, 2, 'Écran', 120.00, '2023-08-01'),
(4, 1, 'Webcam', 45.00, '2023-08-20'),
(5, 4, 'Casque', 80.00, '2023-09-05'),
(6, 2, 'Clavier', 55.00, '2023-09-15'),
(7, 5, 'Souris', 30.00, '2023-10-01'),
(8, 4, 'Écran', 150.00, '2023-10-20');
""")
print("✅ Tables créées : clients (5 lignes), commandes (8 lignes)")| Catégorie | Commande | Description | Exemple |
|---|---|---|---|
| Lecture | SELECT |
Sélectionner des colonnes | SELECT nom, email FROM clients |
SELECT * |
Toutes les colonnes | SELECT * FROM clients |
|
DISTINCT |
Valeurs uniques | SELECT DISTINCT pays FROM clients |
|
| Filtrage | WHERE |
Filtrer les lignes | WHERE pays = 'France' |
AND / OR |
Conditions multiples | WHERE age > 18 AND pays = 'France' |
|
IN |
Liste de valeurs | WHERE pays IN ('France', 'Belgique') |
|
BETWEEN |
Plage de valeurs | WHERE montant BETWEEN 50 AND 100 |
|
LIKE |
Recherche pattern | WHERE nom LIKE 'A%' |
|
IS NULL |
Valeurs nulles | WHERE email IS NULL |
|
| Tri | ORDER BY |
Trier les résultats | ORDER BY nom ASC |
LIMIT |
Limiter le nombre | LIMIT 10 |
|
| Agrégation | COUNT() |
Compter | SELECT COUNT(*) FROM clients |
SUM() |
Somme | SELECT SUM(montant) FROM commandes |
|
AVG() |
Moyenne | SELECT AVG(montant) FROM commandes |
|
MIN() / MAX() |
Min / Max | SELECT MAX(montant) FROM commandes |
|
| Groupement | GROUP BY |
Regrouper | GROUP BY pays |
HAVING |
Filtrer après agrégation | HAVING COUNT(*) > 5 |
|
| Jointures | JOIN |
Jointure interne | JOIN commandes ON ... |
LEFT JOIN |
Jointure gauche | LEFT JOIN commandes ON ... |
# HAVING — Filtrer après agrégation
# Clients ayant dépensé plus de 100€
query = """
SELECT c.nom, SUM(cmd.montant) AS total_achats
FROM clients c
JOIN commandes cmd ON c.id_client = cmd.id_client
GROUP BY c.nom
HAVING SUM(cmd.montant) > 100
ORDER BY total_achats DESC
"""
print("🏆 Clients ayant dépensé plus de 100€ :")
con.execute(query).fetchdf()┌──────────────────────────────────────────────────────────────────┐
│ INNER JOIN (JOIN) │ Seulement les correspondances │
│ A ∩ B │ │
├──────────────────────────────────────────────────────────────────┤
│ LEFT JOIN │ Tout A + correspondances B │
│ A + (A ∩ B) │ (NULL si pas de correspondance) │
├──────────────────────────────────────────────────────────────────┤
│ RIGHT JOIN │ Tout B + correspondances A │
│ B + (A ∩ B) │ │
├──────────────────────────────────────────────────────────────────┤
│ FULL OUTER JOIN │ Tout A + Tout B │
│ A ∪ B │ │
└──────────────────────────────────────────────────────────────────┘
| Fonction | Description | Exemple |
|---|---|---|
CURRENT_DATE |
Date du jour | 2024-01-15 |
EXTRACT() |
Extraire une partie | EXTRACT(YEAR FROM date) → 2024 |
DATE_TRUNC() |
Tronquer à une période | DATE_TRUNC('month', date) → 2024-01-01 |
DATE_DIFF() |
Différence entre dates | DATE_DIFF('day', date1, date2) |
# Commandes par jour de la semaine
query = """
SELECT
EXTRACT(DOW FROM date_commande) AS jour_num,
CASE EXTRACT(DOW FROM date_commande)
WHEN 0 THEN 'Dimanche'
WHEN 1 THEN 'Lundi'
WHEN 2 THEN 'Mardi'
WHEN 3 THEN 'Mercredi'
WHEN 4 THEN 'Jeudi'
WHEN 5 THEN 'Vendredi'
WHEN 6 THEN 'Samedi'
END AS jour,
COUNT(*) AS nb_commandes
FROM commandes
GROUP BY EXTRACT(DOW FROM date_commande)
ORDER BY jour_num
"""
con.execute(query).fetchdf()# CASE — Catégoriser les clients
query = """
SELECT
c.nom,
SUM(cmd.montant) AS total_achats,
CASE
WHEN SUM(cmd.montant) >= 150 THEN '🥇 Premium'
WHEN SUM(cmd.montant) >= 100 THEN '🥈 Standard'
ELSE '🥉 Basique'
END AS categorie
FROM clients c
JOIN commandes cmd ON c.id_client = cmd.id_client
GROUP BY c.nom
ORDER BY total_achats DESC
"""
con.execute(query).fetchdf()Les CTEs permettent de créer des “tables temporaires” pour clarifier les requêtes complexes.
# CTE — Calcul intermédiaire réutilisable
query = """
WITH total_par_client AS (
SELECT
c.id_client,
c.nom,
c.pays,
SUM(cmd.montant) AS total
FROM clients c
JOIN commandes cmd ON c.id_client = cmd.id_client
GROUP BY c.id_client, c.nom, c.pays
)
SELECT
nom,
pays,
total,
CASE WHEN total > 100 THEN '✅ VIP' ELSE '❌' END AS vip
FROM total_par_client
ORDER BY total DESC
"""
con.execute(query).fetchdf()Les Window Functions permettent de faire des calculs sur un groupe de lignes sans les regrouper.
| Fonction | Description |
|---|---|
ROW_NUMBER() |
Numéro de ligne |
RANK() |
Classement (avec ex-aequo) |
DENSE_RANK() |
Classement sans saut |
LAG() |
Valeur de la ligne précédente |
LEAD() |
Valeur de la ligne suivante |
SUM() OVER() |
Somme cumulative |
# LAG — Comparer avec la période précédente
query = """
WITH mensuel AS (
SELECT
DATE_TRUNC('month', date_commande) AS mois,
SUM(montant) AS total
FROM commandes
GROUP BY DATE_TRUNC('month', date_commande)
)
SELECT
mois,
total,
LAG(total) OVER (ORDER BY mois) AS total_precedent,
total - LAG(total) OVER (ORDER BY mois) AS evolution
FROM mensuel
ORDER BY mois
"""
print(" Évolution mensuelle :")
con.execute(query).fetchdf()DuckDB s’intègre parfaitement avec Pandas !
import pandas as pd
# Récupérer le résultat en DataFrame Pandas
query = """
SELECT c.nom, c.pays, SUM(cmd.montant) AS total
FROM clients c
JOIN commandes cmd ON c.id_client = cmd.id_client
GROUP BY c.nom, c.pays
ORDER BY total DESC
"""
df = con.execute(query).fetchdf()
print(type(df)) # C'est un DataFrame Pandas !
df# Requêter directement un DataFrame Pandas avec SQL !
df_exemple = pd.DataFrame({
'produit': ['A', 'B', 'C', 'A', 'B'],
'ventes': [100, 200, 150, 120, 180]
})
# DuckDB peut requêter le DataFrame directement
result = duckdb.query("""
SELECT produit, SUM(ventes) AS total
FROM df_exemple
GROUP BY produit
ORDER BY total DESC
""").fetchdf()
print("SQL directement sur un DataFrame Pandas :")
result# DuckDB peut aussi lire directement des fichiers CSV/Parquet
# Exemple (si tu as un fichier) :
# duckdb.query("SELECT * FROM 'mon_fichier.csv' LIMIT 10")
# duckdb.query("SELECT * FROM 'data.parquet' WHERE date > '2024-01-01'")
print("💡 DuckDB peut lire directement :")
print(" - CSV : SELECT * FROM 'fichier.csv'")
print(" - Parquet : SELECT * FROM 'fichier.parquet'")
print(" - JSON : SELECT * FROM 'fichier.json'")Essaie de résoudre ces exercices dans les cellules de code ci-dessous !
Afficher tous les clients de France, triés par nom.
Calculer le montant moyen des commandes.
Afficher le total des achats par client, y compris ceux sans commande (afficher 0).
Calculer les ventes par mois avec DATE_TRUNC.
Pour chaque client, afficher sa dernière commande (produit et date). Indice : ROW_NUMBER()
clients ?SHOW * FROM clients;SELECT * FROM clients;LIST * FROM clients;DISPLAY * FROM clients;SELECT * FROM clients; affiche toutes les colonnes.
WHERE et HAVING ?WHERE s’utilise avant GROUP BY, HAVING aprèsHAVING est plus rapideWHERE ne peut filtrer que les nombresWHERE filtre avant agrégation, HAVING filtre après.
LEFT JOINRIGHT JOININNER JOINFULL OUTER JOININNER JOIN ne garde que les correspondances.
WITH) ?COUNT()RANK()SUM()GROUP BYRANK() OVER (ORDER BY ...) attribue un classement.
Tu maîtrises maintenant SQL et sais l’exécuter depuis Python ! Découvrons les concepts du Big Data et les bases NoSQL.
👉 Module suivant : 08_intro_big_data_nosql — Big Data, traitement distribué et NoSQL
🎉 Félicitations ! Tu as terminé le module SQL pour Data Engineers.