Apprendre / Aide-mémoire
Aide-mémoire SQL pour testeurs
Le SQL dont les testeurs se servent pour vérifier les données derrière une UI ou une API : retrouver l’enregistrement, vérifier qu’il a changé, et traquer les doublons, orphelins et NULL qui ne devraient pas être là. La syntaxe est du SQL standard qui fonctionne sur PostgreSQL, MySQL 8 et SQLite. Lorsqu’une base de données diffère, la note le précise.
Vérifié avec SQL standard ; vérifié sur PostgreSQL 16, MySQL 8 et SQLite 3
Trouver des lignes
| Tâche | Code |
|---|---|
| Colonnes d’une table | SELECT id, email FROM users; |
| Filtrer | SELECT * FROM orders WHERE status = 'paid' AND total > 100; |
| Une valeur parmi plusieurs | WHERE status IN ('paid', 'shipped') |
| Intervalle (bornes incluses) | WHERE created_at BETWEEN '2026-01-01' AND '2026-01-31' |
| Motif | WHERE email LIKE '%@example.com' |
| Valeur manquante | WHERE phone IS NULL |
| Trier, plus récent d’abord | ORDER BY created_at DESC |
| 10 premières lignes | LIMIT 10 |
| Valeurs distinctes | SELECT DISTINCT status FROM orders; |
SQL Server utilise SELECT TOP 10 au lieu de LIMIT. Sur des timestamps, BETWEEN n’inclut que minuit de la date de fin ; utilisez < lendemain pour couvrir des journées entières.
Agréger
| Tâche | Code |
|---|---|
| Compter les lignes | SELECT COUNT(*) FROM orders; |
| Compter les valeurs non NULL | SELECT COUNT(shipped_at) FROM orders; |
| Compter les valeurs distinctes | SELECT COUNT(DISTINCT user_id) FROM orders; |
| Totaux par groupe | SELECT status, COUNT(*), SUM(total) FROM orders GROUP BY status; |
| Filtrer les groupes | SELECT user_id, COUNT(*) FROM orders GROUP BY user_id HAVING COUNT(*) > 5; |
| Min, max, moyenne | SELECT MIN(total), MAX(total), AVG(total) FROM orders; |
WHERE filtre les lignes avant le regroupement ; HAVING filtre les groupes après.
Jointures
| Tâche | Code |
|---|---|
| Lignes correspondantes uniquement | SELECT o.id, u.email FROM orders o JOIN users u ON u.id = o.user_id; |
| Garder toutes les lignes de gauche | SELECT u.email, o.id FROM users u LEFT JOIN orders o ON o.user_id = u.id; |
| Lignes sans correspondance (anti-jointure) | SELECT u.* FROM users u LEFT JOIN orders o ON o.user_id = u.id WHERE o.id IS NULL; |
| Idem, avec NOT EXISTS | SELECT * FROM users u WHERE NOT EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id); |
Une jointure sur une clé un-à-plusieurs répète le côté « un ». Si un SUM() semble trop élevé après une jointure, c’est que la jointure a démultiplié les lignes.
Contrôles de données des testeurs
| Tâche | Code |
|---|---|
| Doublons | SELECT email, COUNT(*) FROM users GROUP BY email HAVING COUNT(*) > 1; |
| Orphelins (enfant sans parent) | SELECT o.* FROM orders o LEFT JOIN users u ON u.id = o.user_id WHERE u.id IS NULL; |
| Champ obligatoire vide | SELECT * FROM users WHERE email IS NULL OR TRIM(email) = ''; |
| Valeurs hors limites | SELECT * FROM order_items WHERE quantity <= 0 OR unit_price < 0; |
| Total d’en-tête ≠ lignes de détail | SELECT o.id FROM orders o JOIN order_items i ON i.order_id = o.id GROUP BY o.id, o.total HAVING o.total <> SUM(i.quantity * i.unit_price); |
| La ligne existe après une action dans l’UI | SELECT * FROM orders WHERE user_id = 42 ORDER BY created_at DESC LIMIT 1; |
Pièges du NULL
| Piège | Ce qui se passe |
|---|---|
| WHERE col = NULL | Jamais vrai. Utilisez IS NULL / IS NOT NULL. |
| WHERE status <> 'paid' | Ignore les lignes où status est NULL. Ajoutez OR status IS NULL si vous les voulez. |
| NOT IN (sous-requête qui renvoie un NULL) | Ne renvoie aucune ligne. Utilisez plutôt NOT EXISTS. |
| COUNT(col) vs COUNT(*) | COUNT(col) ignore les NULL, COUNT(*) compte toutes les lignes. |
| Arithmétique avec NULL | price + NULL vaut NULL. Enveloppez avec COALESCE(col, 0) si 0 est la bonne valeur par défaut. |
Fonctions de fenêtrage
| Tâche | Code |
|---|---|
| Numéroter les lignes par groupe | ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) |
| Dernière ligne par groupe | SELECT * FROM (SELECT o.*, ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY created_at DESC) AS rn FROM orders o) t WHERE rn = 1; |
| Classement avec ex æquo | RANK() OVER (ORDER BY total DESC) / DENSE_RANK() … |
| Valeur de la ligne précédente | LAG(status) OVER (PARTITION BY test_name ORDER BY run_at) |
| Total cumulé | SUM(total) OVER (ORDER BY created_at) |
Pris en charge par PostgreSQL, MySQL 8+, SQLite 3.25+ et SQL Server. Une fonction de fenêtrage ne peut pas être utilisée dans WHERE : filtrez dans une requête englobante.
Modifier les données sans risque
| Tâche | Code |
|---|---|
| Insérer | INSERT INTO users (email, name) VALUES ('qa+1@example.com', 'QA One'); |
| Mettre à jour (toujours avec WHERE) | UPDATE orders SET status = 'cancelled' WHERE id = 1043; |
| Supprimer (toujours avec WHERE) | DELETE FROM sessions WHERE user_id = 42; |
| Essayer, puis annuler | BEGIN; UPDATE orders SET status = 'cancelled' WHERE id = 1043; SELECT status FROM orders WHERE id = 1043; ROLLBACK; |
Exécutez d’abord le même WHERE dans un SELECT et vérifiez le nombre de lignes avant tout UPDATE ou DELETE.