%% nav-debut : généré par gen_navigation.py, ne pas modifier à la main %% > [!nav] > ↩ [[Code et Développement/Développement d'Applications/SQL I/SQL I|Accueil · SQL I]] > > | [[Code et Développement/Développement d'Applications/SQL I/04_Requêtes sur 2 tables\|← Requêtes sur 2 tables]] | [[Code et Développement/Développement d'Applications/SQL I/06_Généralisation_Requête sur n tables\|Généralisation_Requête sur n tables →]] | > |:---|---:| %% nav-fin %% [[Code et Développement/Développement d'Applications/SQL I/04_Requêtes sur 2 tables|Requêtes sur 2 tables]] # Méthodologie : déterminer et construire les requêtes ## Introduction Écrire une requête SQL sans réflexion préalable est une erreur. ***Le SQL est la traduction technique d’un raisonnement logique.*** >[!tip] Avant d’écrire du code, il faut : > >1. Comprendre ce que représente un enregistrement attendu. >2. Identifier les données nécessaires. >3. Vérifier si ces données existent. >4. Déterminer si certaines doivent être calculées. >5. Organiser les requêtes dans le bon ordre. --- ## Objectifs À la fin de ce chapitre, vous serez capable de : - Analyser une demande. - Identifier les tables nécessaires. - Vérifier si les données existent. - Déterminer si des données doivent être calculées. - Découper un problème en plusieurs requêtes. - Construire la requête finale proprement. --- ## 1. Étape 1 — Comprendre la demande ### 1.1 Ouvrir et comprendre le schéma relationnel Toujours commencer par : - Identifier les tables - Identifier les clés primaires - Identifier les clés étrangères - Comprendre les relations Si vous ne regardez pas le schéma, vous travaillez au hasard. --- ### 1.2 Déterminer ce que représente un enregistrement Il est important de comprendre ce que vous voulez avoir au final. >[!tip] Question obligatoire : > > Un enregistrement du résultat correspond à quoi ? > >Exemples : >- Un client ? >- Une commande ? >- Un client avec son nombre de commandes ? >- Une ville avec son chiffre d’affaires ? >[!warning] Puis-je passer à la suite ? >Si ce point n’est pas clair, vous ne pouvez pas écrire la requête. --- ## 2. Étape 2 — Les données existent-elles ? Maintenant que je sais ce que je veux, je dois savoir si je vais pouvoir résoudre le problème : les données sont-elles dans la base de données ou puis-je les calculer à l'aide de ces données ? ### 2.1 Données déjà présentes Si les données existent telles quelles, une simple sélection suffit. >[!tip] Exemples > >- nom_client → table `Clients` >- date_commande → table `Commandes` >- montant_total → table `Commandes` --- ### 2.2 Données à calculer Si je ne trouve pas la donnée dans un champ d'une table, je vais chercher à savoir si je peux la calculer à l'aide de données bien présentes dans la base de données. >[!tip] Exemples >- Nombre de commandes → `COUNT` >- Total des ventes → `SUM` >- Moyenne → `AVG` >[!tip] Fonctions d'agrégat >Quand la donnée calculée résume plusieurs enregistrements (un nombre, un total, une moyenne…), elle implique généralement : >- `GROUP BY` >- éventuellement `HAVING` > >En revanche, un calcul fait enregistrement par enregistrement (par exemple `Salaire * 1.10` ou `CHAR_LENGTH(titre)`) n'a pas besoin de `GROUP BY`. --- ## 3. Étape 3 — Combien de requêtes ? ### Cas simple Une seule requête suffit si : - Toutes les données sont accessibles directement - Les calculs peuvent être faits dans la même requête --- ### Cas complexe Plusieurs requêtes sont nécessaires si : - Une table nécessaire est le résultat d’un calcul - Une requête doit servir d’entrée à une autre - La logique est plus claire en plusieurs étapes Si une table est le résultat d’une requête, on applique la méthode à cette requête intermédiaire. --- ## 4. Étape 4 — Préparer la requête Toujours réfléchir dans cet ordre : 1. `FROM` — Quelles tables ? 2. `WHERE` — Quels filtres ? 3. `GROUP BY` — Faut-il regrouper ? 4. `HAVING` — Faut-il filtrer les groupes ? 5. `ORDER BY` — Faut-il trier ? 6. `SELECT` — Que veut-on afficher ? Attention : ordre de réflexion ≠ ordre d’écriture SQL. Pour vous aider, nous vous proposons d'utiliser le schéma ci-dessous : ![[Code et Développement/Développement d'Applications/SQL I/ressources/Preparer_sa_requete.pdf]] Une fois rempli et fait pour chaque requête, ce schéma vous permettra d'écrire rapidement vos requêtes. >[!tip] Corrections des ateliers > Vous verrez que nous utilisons ce schéma pour la correction des exercices. ## 5. Exemple d'une demande amenant à une requête complexe En utilisant la description des deux tables suivantes, on nous demande la liste des clients dont le chiffre d’affaires total est supérieur à la moyenne du chiffre d’affaires de tous les clients. ### La table *Clients* | id_client | nom_client | ville_client | date_inscription | | --------- | ---------- | ------------ | ---------------- | | 1 | Martin | Paris | 2022-03-15 | | 2 | Dupont | Lyon | 2021-11-02 | | 3 | Bernard | Marseille | 2023-01-20 | | 4 | Petit | Lille | 2022-07-08 | | 5 | Robert | Bordeaux | 2023-05-12 | ### La table *Commandes* | id_commande | id_client | date_commande | montant_total | | ----------- | --------- | ------------- | ------------- | | 101 | 1 | 2023-01-10 | 250.00 | | 102 | 1 | 2023-02-18 | 120.00 | | 103 | 2 | 2023-03-05 | 560.00 | | 104 | 2 | 2023-04-22 | 80.00 | | 105 | 2 | 2023-06-01 | 310.00 | | 106 | 3 | 2023-07-14 | 150.00 | | 107 | 4 | 2023-08-03 | 400.00 | ### Ai-je les données pour répondre à cette demande ? Dans les tables, je ne trouve ni le chiffre d'affaires total des clients, ni la moyenne du chiffre d’affaires de tous les clients. Puis-je les calculer ? 1. Le chiffre d'affaires total d'un client peut se calculer en faisant la somme des *montant_total*. 2. La moyenne du chiffre d’affaires de tous les clients peut se calculer après avoir fait le calcul du point 1. Je peux donc en conclure que je peux répondre à la demande. ### Combien de requêtes me faudra-t-il ? 1. Il me faut ***une première requête*** qui calcule **les chiffres d'affaires par client** et les compare à **la moyenne des chiffres d'affaires**. Cette requête aura en entrée les tables *Clients* et *Commandes* et une deuxième requête. 2. En effet, la moyenne des chiffres d'affaires n'étant pas présente dans la base de données, je dois faire ***une deuxième requête*** qui retourne cette valeur. 3. En entrée de cette requête, il faut avoir le chiffre d'affaires de chaque client, que l'on va obtenir à l'aide d'***une troisième requête***. > [!warning] Attention au piège : « tous les clients » > La demande porte sur la moyenne du chiffre d'affaires de **tous** les clients. Or *Robert* n'a passé aucune commande : son chiffre d'affaires est de 0 et il doit compter dans la moyenne. > > Dans la troisième requête, il faut donc : > 1. Garder tous les clients, même sans commande : on utilise une jointure dissymétrique `Clients LEFT JOIN Commandes` (voir [[Code et Développement/Développement d'Applications/SQL I/04_Requêtes sur 2 tables|Requêtes sur 2 tables]]). > 2. Remplacer la somme de Robert, qui vaut `NULL` (il n'a aucun montant à additionner), par 0 : on utilise `IFNULL(SUM(montant_total), 0)` (voir [[Code et Développement/Développement d'Applications/SQL I/07_Fonctions utiles#Les Fonctions Conditionnelles|Fonctions utiles]]). Sans cela, `AVG` ignorerait la valeur `NULL` et Robert ne serait pas compté. > > | Troisième requête | Moyenne obtenue | > | :--- | :--- | > | `INNER JOIN` (Robert disparaît) | 467,5 | > | `LEFT JOIN` sans `IFNULL` (Robert vaut `NULL`, ignoré par `AVG`) | 467,5 | > | `LEFT JOIN` avec `IFNULL(…, 0)` (Robert compte pour 0) | **374** | > > Seule la dernière version répond à la demande. Ce qui donne le diagramme suivant : ```mermaid graph TD A@{ shape: text, label: "Clients" }-->C["3ème Requête"] B@{ shape: text, label: "Commandes" }-->C C-->D@{ shape: text, label: "Chiffre d'affaires par client" } D-->E["2ème Requête"] E-->F@{ shape: text, label: "Moyenne des chiffres d'affaires des clients" } G@{ shape: text, label: "Clients" }-->I["1ère Requête"] H@{ shape: text, label: "Commandes" }-->I F-->I I-->J@{ shape: text, label: "Clients dont le chiffre d'affaires est supérieur à la moyenne" } ``` ### Préparation des requêtes Avant de les écrire, nous vous conseillons de préparer chaque requête pour : 1. Bien comprendre ce que vous faites. 2. Ne pas passer trop de temps à taper du code SQL. #### Préparation de la 3ème requête ![[Code et Développement/Développement d'Applications/SQL I/ressources/cours01.png|500]] #### Préparation de la 2ème requête ![[Code et Développement/Développement d'Applications/SQL I/ressources/cours02.png|500]] #### Préparation de la 1ère requête ![[Code et Développement/Développement d'Applications/SQL I/ressources/cours03.png|500]] ### Requête finale ```sql SELECT -- Première requête : clients dont le CA > CA moyen C.id_client, C.nom_client, SUM(Co.montant_total) AS chiffre_affaires FROM Clients C INNER JOIN Commandes Co ON C.id_client = Co.id_client , ( -- Deuxième requête : moyenne du chiffre d’affaires par client SELECT AVG(ca_client) AS moyenne_ca FROM ( -- Troisième requête : chiffres d'affaires par client SELECT IFNULL(SUM(Co2.montant_total), 0) AS ca_client FROM Clients C2 LEFT JOIN Commandes Co2 ON C2.id_client = Co2.id_client GROUP BY C2.id_client ) AS t ) AS m GROUP BY C.id_client, C.nom_client, m.moyenne_ca HAVING SUM(Co.montant_total) > m.moyenne_ca; ``` > [!note] La virgule dans le `FROM` > La virgule placée après la jointure réalise un **produit cartésien** (voir [[Code et Développement/Développement d'Applications/SQL I/04_Requêtes sur 2 tables|Requêtes sur 2 tables]]) entre les commandes de chaque client et le résultat de la deuxième requête. Ce résultat ne contient **qu'une seule ligne** (la moyenne) : le produit cartésien revient donc simplement à ajouter la moyenne `moyenne_ca` à chaque enregistrement, ce qui permet de la comparer au chiffre d'affaires dans le `HAVING`. ### Résultats **Troisième requête** : le chiffre d'affaires de chaque client (le champ `id_client` est ajouté ici pour la lisibilité ; la requête n'affiche que `ca_client`). | id_client | ca_client | | :-- | :-- | | 1 | 370.00 | | 2 | 950.00 | | 3 | 150.00 | | 4 | 400.00 | | 5 | 0.00 | **Deuxième requête** : la moyenne des chiffres d'affaires, $(370 + 950 + 150 + 400 + 0) / 5 = 374$. | moyenne_ca | | :-- | | 374.000000 | **Première requête** : les clients dont le chiffre d'affaires est supérieur à 374. | id_client | nom_client | chiffre_affaires | | :-- | :-- | :-- | | 2 | Dupont | 950.00 | | 4 | Petit | 400.00 | Dans le chapitre d'après, nous allons généraliser la lecture du `FROM` avec plusieurs tables : [[Code et Développement/Développement d'Applications/SQL I/06_Généralisation_Requête sur n tables|Généralisation_Requête sur n tables]] %% nav-debut : généré par gen_navigation.py, ne pas modifier à la main %% > [!nav] > ↩ [[Code et Développement/Développement d'Applications/SQL I/SQL I|Accueil · SQL I]] > > | [[Code et Développement/Développement d'Applications/SQL I/04_Requêtes sur 2 tables\|← Requêtes sur 2 tables]] | [[Code et Développement/Développement d'Applications/SQL I/06_Généralisation_Requête sur n tables\|Généralisation_Requête sur n tables →]] | > |:---|---:| %% nav-fin %% --- 🗓️ Historique Dernière mise à jour : `21 octobre 2026` Rédigé par : [[Vincent TELLIER]]