SQL dans tes programmes
Modifier les données
Jusqu'ici, on a surtout lu. Trois commandes modifient les données :
| Commande | Effet | Exemple |
|---|---|---|
INSERT INTO |
ajoute une ligne | INSERT INTO vendeurs (nom, telephone) VALUES ('Junior', '699001122') |
UPDATE ... SET |
modifie des lignes | UPDATE produits SET prix_vente = 1300 WHERE nom = 'riz' |
DELETE FROM |
supprime des lignes | DELETE FROM ventes WHERE id = 12 |
Le danger : l'injection SQL
Voici une fonction de connexion, comme on en voit encore dans des sites mal écrits :
def connexion(nom, mot_de_passe):
requete = f"SELECT * FROM comptes WHERE nom = '{nom}' AND mdp = '{mot_de_passe}'"
return len(conn.execute(requete).fetchall()) > 0
Elle a l'air correcte. Mais que se passe-t-il si quelqu'un tape, comme mot de passe : ' OR '1'='1 ? La requête devient :
SELECT * FROM comptes WHERE nom = 'Awa' AND mdp = '' OR '1'='1'
'1'='1' est toujours vrai, donc la condition est vraie pour toutes les lignes : l'attaquant entre sans connaître le mot de passe.
La parade : les paramètres
On ne colle jamais une donnée de l'utilisateur dans le texte d'une requête. On met des points d'interrogation à la place, et on passe les valeurs à part :
requete = "SELECT * FROM comptes WHERE nom = ? AND mdp = ?"
conn.execute(requete, (nom, mot_de_passe))
(Un vrai site ne stocke d'ailleurs jamais les mots de passe en clair : il stocke leur empreinte, ou hachage. Tu verras ça dans le module Sécurité.)
Exercice : Corriger la faille
La fonction connexion ci-dessous est vulnérable. Lance le code : l'attaque réussit. Puis corrige la fonction avec une requête paramétrée.
Voir une solution
import sqlite3
conn = sqlite3.connect(":memory:")
conn.execute("CREATE TABLE comptes (nom TEXT, mdp TEXT)")
conn.execute("INSERT INTO comptes VALUES ('Awa', 'ndole2026')")
conn.execute("INSERT INTO comptes VALUES ('Paul', 'eru-et-waterfufu')")
def connexion(nom, mot_de_passe):
"""Renvoie True si le nom et le mot de passe correspondent à un compte."""
requete = "SELECT * FROM comptes WHERE nom = ? AND mdp = ?"
return len(conn.execute(requete, (nom, mot_de_passe)).fetchall()) > 0
print("Bon mot de passe :", connexion("Awa", "ndole2026"))
print("Mauvais mot de passe :", connexion("Awa", "1234"))
print("Attaque :", connexion("Awa", "' OR '1'='1"))Dans l'appli, ton code est exécuté et corrigé automatiquement. Faire l'exercice
Exercice : Enregistrer une vente proprement
Écris ajouter_vente(date, produit_id, vendeur_id, quantite) qui insère une vente dans la table ventes de boutique2.sql avec une requête paramétrée, puis valide avec conn.commit().
La colonne id est une clé primaire entière : si on ne la donne pas, SQLite lui attribue automatiquement le numéro suivant. Il faut donc nommer les colonnes qu'on remplit : INSERT INTO ventes (date, produit_id, vendeur_id, quantite) VALUES (?, ?, ?, ?).
Voir une solution
import sqlite3
conn = sqlite3.connect(":memory:")
conn.executescript(open("boutique2.sql", encoding="utf-8").read())
def ajouter_vente(date, produit_id, vendeur_id, quantite):
"""Enregistre une vente avec une requête paramétrée."""
conn.execute(
"INSERT INTO ventes (date, produit_id, vendeur_id, quantite) VALUES (?, ?, ?, ?)",
(date, produit_id, vendeur_id, quantite),
)
conn.commit()
ajouter_vente("2026-04-01", 7, 4, 3) # Junior vend 3 sachets de sel
print(conn.execute("SELECT * FROM ventes ORDER BY id DESC LIMIT 1").fetchall())Dans l'appli, ton code est exécuté et corrigé automatiquement. Faire l'exercice
SQL et pandas ensemble
Dans la vraie vie, on combine souvent les deux : SQL pour extraire les bonnes données d'une grosse base, pandas pour analyser le résultat.
import pandas as pd
df = pd.read_sql_query("""
SELECT v.date, p.nom AS produit, d.nom AS vendeur, v.quantite, p.prix_vente
FROM ventes v
JOIN produits p ON v.produit_id = p.id
JOIN vendeurs d ON v.vendeur_id = d.id
""", conn)
À partir de là, tout le module pandas s'applique. Faut-il regrouper en SQL ou en pandas ? Règle pratique : quand la base est énorme et distante, on fait faire le maximum de travail à la base (filtrer, regrouper), pour ne rapatrier que le résultat. Quand les données tiennent en mémoire, on choisit ce qui est le plus lisible.
Exercice : De la base au DataFrame
Avec pd.read_sql_query, récupère dans df toutes les ventes avec ces colonnes, dans cet ordre : date, produit (le nom du produit), vendeur (le nom du vendeur), quantite, prix_vente.
Puis, avec pandas, calcule ca_vendeur : un dictionnaire du chiffre d'affaires (quantité x prix de vente) par vendeur.
Voir une solution
import sqlite3
conn = sqlite3.connect(":memory:")
conn.executescript(open("boutique2.sql", encoding="utf-8").read())
import pandas as pd
df = pd.read_sql_query("""
SELECT v.date, p.nom AS produit, d.nom AS vendeur, v.quantite, p.prix_vente
FROM ventes v
JOIN produits p ON v.produit_id = p.id
JOIN vendeurs d ON v.vendeur_id = d.id
""", conn)
df["montant"] = df["quantite"] * df["prix_vente"]
ca_vendeur = df.groupby("vendeur")["montant"].sum().to_dict()
print(df.head())
print(ca_vendeur)Dans l'appli, ton code est exécuté et corrigé automatiquement. Faire l'exercice