Project Oxygen & Ideo-LabIDEO LAB Dashboard 2026

đŸ—ƒïž SQL – Le Langage de Base de DonnĂ©es (DDL, DML, DQL)

Guide complet IDEO‑Lab : SELECT, JOIN, GROUP BY, Subqueries, UNION & EXPLAIN.

1.1 Facile

Vue d'ensemble

SQL (Structured Query Language), SGBDR, DDL, DML, DQL.

SQL SGBDR
1.2 Facile

DDL (Data Definition)

CREATE, ALTER, DROP (Tables, DBs).

DDL CREATE TABLE
1.3 Moyen

Contraintes (DDL)

PRIMARY KEY, FOREIGN KEY, UNIQUE, NOT NULL.

PRIMARY KEY FOREIGN KEY
2.1 Facile

DML (INSERT, UPDATE, DELETE)

Commandes de manipulation (Create, Update, Delete).

INSERT UPDATE DELETE
2.2 Facile

DQL : SELECT (Read)

SELECT, FROM, *, AS (Alias).

DQL SELECT
2.3 Facile

Clauses WHERE (Filtres)

AND, OR, IN, BETWEEN, LIKE, IS NULL.

WHERE LIKE
2.4 Facile

Tri & Limite

ORDER BY (ASC/DESC), LIMIT, OFFSET.

ORDER BY LIMIT
3.1 Moyen

Agrégats & GROUP BY

COUNT(), SUM(), AVG(), GROUP BY. La base de l'analyse.

GROUP BY COUNT
3.2 Moyen

Clause HAVING

Filtrer *aprĂšs* un GROUP BY. (WHERE filtre *avant*).

HAVING vs WHERE
3.3 Moyen

Jointures (JOIN)

INNER JOIN, LEFT/RIGHT JOIN, FULL OUTER JOIN.

INNER JOIN LEFT JOIN
3.4 Moyen

Opérateurs (UNION)

UNION (Combine les résultats, supprime doublons), UNION ALL (garde doublons).

UNION UNION ALL
4.1 Avancé

Sous-RequĂȘtes (ImbriquĂ©es)

IN, EXISTS, (SELECT ... FROM (SELECT ...)).

Subquery IN EXISTS
4.2 Avancé

CTEs (WITH)

Common Table Expressions (WITH ... AS ...). Plus lisible.

CTE WITH
4.3 Avancé

Fonctions FenĂȘtrĂ©es

ROW_NUMBER(), PARTITION BY, OVER().

Window PARTITION BY
5.1 Moyen

DCL & TCL

GRANT, REVOKE (DCL). COMMIT, ROLLBACK (TCL).

GRANT COMMIT
5.2 Moyen

Index (CREATE INDEX)

CREATE INDEX, B-Tree, Index Composé.

INDEX Performance
5.3 Avancé

EXPLAIN (Optimisation)

EXPLAIN, Full Table Scan vs Index Scan.

EXPLAIN Tuning
6.1 Facile

Cheat-sheet SQL

Commandes DQL (SELECT) & DML (INSERT...).

cheat SQL
1.1 Vue d'ensemble : SQL
Qu'est-ce que SQL ?

SQL (Structured Query Language) est le langage de programmation **déclaratif** standardisé utilisé pour créer, gérer et interroger des **bases de données relationnelles** (SGBDR).

Déclaratif : Vous décrivez **CE QUE** vous voulez (ex: "le nom des clients à Paris"), et non **COMMENT** l'obtenir (ex: "ouvre le fichier X, boucle, compare..."). Le SGBDR s'occupe de l'optimisation.

Les 4 sous-langages SQL
SigleNomCommandesBut
DDLData Definition LanguageCREATE, ALTER, DROPDéfinir la structure (tables, index).
DMLData Manipulation LanguageINSERT, UPDATE, DELETEManipuler les données (lignes).
DQLData Query LanguageSELECTInterroger/Lire les données.
DCLData Control LanguageGRANT, REVOKEGérer les permissions.
TCLTransaction ControlCOMMIT, ROLLBACKGérer les transactions.
ModĂšle Relationnel (Tables)

Un SGBDR (SystÚme de Gestion de BDD Relationnelle) stocke les données dans des tables (relations), composées de lignes (tuples) et de colonnes (attributs). Les clés (primaires/étrangÚres) lient ces tables entre elles.

Table: 'utilisateurs'
+----+---------+
| id | nom     |
+----+---------+
| 1  | Alice   |
| 2  | Bob     |
+----+---------+

Table: 'commandes'
+----+---------+------------+
| id | user_id | montant    | (user_id est ClĂ© ÉtrangĂšre
+----+---------+------------+  vers utilisateurs.id)
| 1  | 2       | 50.00      |
| 2  | 1       | 75.00      |
+----+---------+------------+
1.2 Dialectes SQL (ANSI vs T-SQL vs PL/pgSQL)
SQL ANSI (Le Standard)

SQL ANSI est la norme (ISO/IEC) qui définit la syntaxe de base (SELECT, INSERT, WHERE, JOIN...). 99% des SGBDR respectent cette base.

Dialectes (Les "Saveurs")

Chaque SGBDR ajoute ses propres fonctions (non-standards) et son propre langage de *scripting* (pour les procédures stockées).

SGBDRDialecte (Langage de script)Exemple de Fonction
PostgreSQLPL/pgSQLNOW(), AGE(), ::date (cast)
MySQL/MariaDB(Procédural)NOW(), DATE_FORMAT(), LIMIT
Microsoft SQL ServerT-SQL (Transact-SQL)GETDATE(), ISNULL(), TOP 10
OraclePL/SQLSYSDATE, NVL(), ROWNUM <= 10

Ce guide se concentre sur le SQL ANSI (PostgreSQL/MariaDB).

1.3 DDL (CREATE, ALTER, DROP)
CREATE DATABASE
CREATE DATABASE ideo_lab_db
    WITH
    ENCODING = 'UTF8'
    LC_COLLATE = 'fr_FR.UTF-8';
CREATE TABLE
CREATE TABLE utilisateurs (
    id INT PRIMARY KEY, -- (Contrainte, voir 1.4)
    email VARCHAR(100) NOT NULL UNIQUE,
    nom VARCHAR(50),
    date_inscription DATE DEFAULT CURRENT_DATE
);
ALTER TABLE (Modifier la structure)
-- Ajouter une colonne
ALTER TABLE utilisateurs
ADD COLUMN age INT DEFAULT 18;

-- Modifier le type d'une colonne (PostgreSQL)
ALTER TABLE utilisateurs
ALTER COLUMN nom TYPE VARCHAR(100);

-- Modifier le type d'une colonne (MySQL)
ALTER TABLE utilisateurs
MODIFY COLUMN nom VARCHAR(100);

-- Renommer une colonne
ALTER TABLE utilisateurs
RENAME COLUMN nom TO nom_complet;

-- Supprimer une colonne
ALTER TABLE utilisateurs
DROP COLUMN age;
DROP (Supprimer)

⚠ DANGER : IrrĂ©versible.

-- Supprime la table ET ses données
DROP TABLE utilisateurs;

-- Supprime la base de données
DROP DATABASE ideo_lab_db;
TRUNCATE (Vider)

⚠ DANGER : Vide entiĂšrement la table (rapide). RĂ©initialise l'auto-incrĂ©ment.

TRUNCATE TABLE logs;
1.4 Contraintes (DDL)

Les contraintes garantissent l'intégrité des données.

ContrainteDescription
PRIMARY KEYIdentifiant unique (non-nul) de la ligne. (Indexé auto).
FOREIGN KEYGarantit que la valeur existe dans une autre table (intégrité référentielle).
UNIQUELa valeur doit ĂȘtre unique dans la colonne (ex: email).
NOT NULLLa colonne ne peut pas ĂȘtre vide (NULL).
CHECKCondition personnalisée (ex: CHECK (age > 18)).
Exemple FOREIGN KEY (Relation)
CREATE TABLE commandes (
    id INT PRIMARY KEY,
    produit VARCHAR(50),
    user_id INT NOT NULL,
    
    -- Contrainte
    CONSTRAINT fk_user
        FOREIGN KEY(user_id) 
        REFERENCES utilisateurs(id)
        ON DELETE CASCADE -- (Si user est supprimé, supprime ses commandes)
);
2.1 DML (INSERT, UPDATE, DELETE)
INSERT (Ajouter des lignes)
-- Syntaxe (complÚte, recommandée)
INSERT INTO utilisateurs (id, email, nom)
VALUES (1, 'alice@mail.com', 'Alice');

-- Syntaxe (multiple)
INSERT INTO utilisateurs (id, email, nom)
VALUES
  (2, 'bob@mail.com', 'Bob'),
  (3, 'charlie@mail.com', 'Charlie');
UPDATE (Modifier des lignes)

⚠ DANGER : Ne **jamais** oublier la clause WHERE. Sans elle, vous mettez Ă  jour toutes les lignes de la table.

-- Modifier 1 ligne
UPDATE utilisateurs
SET nom = 'Alice Dupont'
WHERE id = 1;

-- Modifier plusieurs champs
UPDATE utilisateurs
SET nom = 'Robert Martin', email = 'bob.m@mail.com'
WHERE id = 2;

-- Modifier (ex: augmenter le prix de 10%)
UPDATE produits
SET prix = prix * 1.10
WHERE categorie = 'electronique';
DELETE (Supprimer des lignes)

⚠ DANGER : Sans WHERE, supprime toutes les lignes (lentement, 1 par 1).

-- Supprimer 1 ligne
DELETE FROM utilisateurs
WHERE id = 3;

-- Supprimer un groupe
DELETE FROM logs
WHERE date_log < '2024-01-01';
2.2 DQL : SELECT (Read)
-- Sélectionner toutes les colonnes
SELECT * FROM utilisateurs;

-- Sélectionner des colonnes spécifiques
SELECT nom, email FROM utilisateurs;

-- Utiliser un Alias (AS)
SELECT nom AS nom_client, email FROM utilisateurs;

-- Sélectionner des valeurs uniques
SELECT DISTINCT status FROM commandes;

-- Ordre d'exécution logique (simplifié)
-- 1. FROM (La/Les tables)
-- 2. WHERE (Filtre lignes)
-- 3. SELECT (Choix colonnes)
-- 4. ORDER BY (Tri final)
2.3 Clauses WHERE (Filtrage)
SELECT * FROM produits
WHERE (categorie = 'Electronique' AND prix > 100)
   OR (categorie = 'Logiciel')

-- Opérateurs
-- ... WHERE prix > 100
-- ... WHERE prix < 100
-- ... WHERE prix >= 100
-- ... WHERE prix != 100 (ou <>)

-- Plage (inclusif)
-- ... WHERE prix BETWEEN 50 AND 100

-- Liste
-- ... WHERE categorie IN ('Electronique', 'Livres')

-- Nul
-- ... WHERE description IS NULL
-- ... WHERE description IS NOT NULL

-- Pattern (Wildcard %)
-- (Commence par 'A')
-- ... WHERE nom LIKE 'A%'
-- (Contient 'bloc')
-- ... WHERE nom LIKE '%bloc%'
-- (CaractĂšre unique '_')
-- ... WHERE nom LIKE 'Al_ce'
2.4 Tri (ORDER BY) & Pagination (LIMIT)
ORDER BY (Tri)
-- Tri alphabétique (Ascendant)
SELECT * FROM utilisateurs
ORDER BY nom ASC;

-- Tri (Descendant)
SELECT * FROM commandes
ORDER BY montant DESC;

-- Tri multiple
SELECT * FROM utilisateurs
ORDER BY nom ASC, date_inscription DESC;
LIMIT & OFFSET (Pagination)

LIMIT (combien) et OFFSET (ignorer combien).

-- Les 10 articles les plus chers
SELECT * FROM produits
ORDER BY prix DESC
LIMIT 10;

-- Pagination (Page 3, 20 par page)
-- (Page 1: LIMIT 20 OFFSET 0)
-- (Page 2: LIMIT 20 OFFSET 20)
SELECT * FROM produits
ORDER BY id
LIMIT 20 OFFSET 40;
3.1 Agrégats & GROUP BY
Fonctions d'Agrégat

Résument un ensemble de lignes en une seule valeur.

-- Compte le nombre de lignes (utilisateurs)
SELECT COUNT(*) FROM utilisateurs;

-- Compte le nombre d'emails (ignore les NULLs)
SELECT COUNT(email) FROM utilisateurs;

-- Compte les emails uniques
SELECT COUNT(DISTINCT email) FROM utilisateurs;

-- Somme totale des montants
SELECT SUM(montant) FROM commandes;

-- Moyenne des montants
SELECT AVG(montant) FROM commandes;

-- Prix Min/Max
SELECT MIN(prix), MAX(prix) FROM produits;
GROUP BY (Regroupement)

GROUP BY "contracte" les lignes qui ont la mĂȘme valeur (ex: mĂȘme user_id) pour que les agrĂ©gats (COUNT, SUM) s'appliquent Ă  ce groupe.

RĂšgle : Si vous utilisez GROUP BY, toute colonne dans le SELECT doit ĂȘtre SOIT dans le GROUP BY, SOIT ĂȘtre une fonction d'agrĂ©gat.

Exemple : Compter les commandes par utilisateur
-- (Tables 'utilisateurs' et 'commandes' de 1.1)

SELECT
    u.nom,
    COUNT(c.id) AS nb_commandes,
    SUM(c.montant) AS total_depense
FROM
    utilisateurs u
JOIN
    commandes c ON u.id = c.user_id
GROUP BY
    u.id, u.nom; -- (On doit grouper par l'ID et le nom)

-- Résultat:
-- nom   | nb_commandes | total_depense
-- ------|--------------|---------------
-- Alice | 1            | 75.00
-- Bob   | 2            | 50.00
3.2 Clause HAVING (vs WHERE)
La différence (Cruciale)

WHERE : Filtre les **lignes** (avant le GROUP BY).

HAVING : Filtre les **groupes** (aprĂšs le GROUP BY).

Ordre d'exécution logique
1. FROM / JOIN
2. WHERE (Filtre les lignes)
3. GROUP BY (Regroupe les lignes)
4. HAVING (Filtre les groupes)
5. SELECT
6. ORDER BY
Exemple

"Afficher le total dĂ©pensĂ© par les clients de Paris, mais *seulement* si ce total est supĂ©rieur Ă  100€."

SELECT
    user_id,
    SUM(montant) AS total_depense
FROM
    commandes
WHERE
    ville_facturation = 'Paris' -- (1. Filtre les lignes avant)
GROUP BY
    user_id
HAVING
    SUM(montant) > 100; -- (2. Filtre les groupes aprĂšs)
3.3 Jointures (JOIN)
Tables d'exemple

Table utilisateurs (Gauche) et commandes (Droite).

utilisateurs (id, nom)
(1, Alice)
(2, Bob)
(3, Charlie)
commandes (id, user_id, produit)
(101, 2, 'Clavier')
(102, 1, 'Souris')
(103, 2, 'Ecran')
(104, 4, 'USB') -- (user_id 4 n'existe pas)
INNER JOIN (Intersection)

Ne retourne que les lignes qui ont une correspondance dans les **deux** tables.

SELECT u.nom, c.produit
FROM utilisateurs u
INNER JOIN commandes c ON u.id = c.user_id;

-- Résultat (3 lignes)
-- nom   | produit
-- ------|----------
-- Bob   | Clavier
-- Alice | Souris
-- Bob   | Ecran
-- (Charlie (sans commande) est exclu)
-- (USB (sans user) est exclu)
LEFT JOIN (Garde la gauche)

Retourne **toutes** les lignes de la table de gauche (utilisateurs), et NULL si pas de correspondance Ă  droite.

SELECT u.nom, c.produit
FROM utilisateurs u
LEFT JOIN commandes c ON u.id = c.user_id;

-- Résultat (4 lignes)
-- nom     | produit
-- --------|----------
-- Alice   | Souris
-- Bob     | Clavier
-- Bob     | Ecran
-- Charlie | NULL
RIGHT JOIN & FULL OUTER JOIN
RIGHT JOIN (Garde la droite)

L'inverse de LEFT. Retourne toutes les commandes, et NULL si pas d'utilisateur.

SELECT u.nom, c.produit
FROM utilisateurs u
RIGHT JOIN commandes c ON u.id = c.user_id;

-- Résultat (4 lignes)
-- nom   | produit
-- ------|----------
-- Bob   | Clavier
-- Alice | Souris
-- Bob   | Ecran
-- NULL  | USB
FULL OUTER JOIN (Garde tout)

Retourne tout (INNER + LEFT + RIGHT).

-- Résultat (5 lignes)
-- nom     | produit
-- --------|----------
-- Alice   | Souris
-- Bob     | Clavier
-- Bob     | Ecran
-- Charlie | NULL
-- NULL    | USB
3.4 Opérateurs (UNION)
UNION

Combine les rĂ©sultats (lignes) de deux requĂȘtes SELECT. (Les colonnes doivent correspondre).

UNION : **Supprime les doublons** (lent).

(SELECT nom FROM clients_paris)
UNION
(SELECT nom FROM clients_lyon)
-- (Retourne 'Dupont' 1 fois)
UNION ALL

Combine **tout** (rapide).

(SELECT nom FROM clients_paris)
UNION ALL
(SELECT nom FROM clients_lyon)
-- (Retourne 'Dupont' 2 fois)
4.1 Sous-RequĂȘtes (ImbriquĂ©es)
Sous-requĂȘte dans WHERE
-- (Trouver les utilisateurs qui ont passé commande)
-- (1. Avec IN)
SELECT nom FROM utilisateurs
WHERE id IN (SELECT DISTINCT user_id FROM commandes);

-- (2. Avec EXISTS - souvent plus performant)
SELECT nom FROM utilisateurs u
WHERE EXISTS (
    SELECT 1 FROM commandes c
    WHERE c.user_id = u.id
);
Sous-requĂȘte dans FROM (Table DĂ©rivĂ©e)

Permet de "pré-filtrer" ou "pré-agréger" des données avant la jointure.

-- (Calculer la moyenne des totaux par client)
SELECT AVG(depenses_client.total)
FROM (
    -- (Cette sous-requĂȘte s'exĂ©cute en premier)
    SELECT user_id, SUM(montant) AS total
    FROM commandes
    GROUP BY user_id
) AS depenses_client;
Sous-requĂȘte dans SELECT (Scalaire)

La sous-requĂȘte ne doit retourner qu'une seule valeur (1 ligne, 1 colonne).

-- (Afficher le nom du user ET sa derniĂšre commande)
SELECT
    u.nom,
    (SELECT MAX(c.date_commande)
     FROM commandes c
     WHERE c.user_id = u.id) AS derniere_commande
FROM
    utilisateurs u;
4.2 CTE (WITH) (Moderne)

Les CTEs (Common Table Expressions) sont une alternative **plus lisible** aux sous-requĂȘtes imbriquĂ©es (surtout celles dans FROM).

On utilise WITH pour définir des "tables temporaires" nommées.

Exemple (remplace 4.1, Tab 2)
-- (Calculer la moyenne des totaux par client)

-- 1. Définir la CTE (table temporaire)
WITH depenses_client AS (
    SELECT
        user_id,
        SUM(montant) AS total
    FROM commandes
    GROUP BY user_id
)
-- 2. Utiliser la CTE
SELECT AVG(depenses_client.total)
FROM depenses_client;
4.3 Fonctions FenĂȘtrĂ©es (OVER, PARTITION BY)

Une fonction fenĂȘtrĂ©e effectue un calcul sur un ensemble de lignes (une "fenĂȘtre") **sans** rĂ©duire le nombre de lignes (contrairement Ă  GROUP BY).

Exemple : Rang (Ranking)

"Numéroter les commandes de chaque client, de la plus chÚre à la moins chÚre."

SELECT
    user_id,
    montant,
    -- (CrĂ©e une "fenĂȘtre" par 'user_id',
    --  la trie par 'montant DESC',
    --  et applique un numéro de ligne)
    ROW_NUMBER() OVER(
        PARTITION BY user_id
        ORDER BY montant DESC
    ) AS rang_depense
FROM
    commandes;

-- Résultat:
-- user_id | montant | rang_depense
-- --------|---------|--------------
-- 1       | 75.00   | 1
-- 2       | 50.00   | 1
-- 2       | 25.00   | 2
5.1 DCL (Permissions) & TCL (Transactions)
DCL (Data Control Language)

Gérer les permissions (qui peut faire quoi).

-- (MariaDB/PostgreSQL)

-- Créer un utilisateur
CREATE USER 'lecteur'@'localhost' IDENTIFIED BY 'pass';

-- Donner des droits
GRANT SELECT ON ideo_lab_db.articles TO 'lecteur'@'localhost';

-- Donner tous les droits (DANGEREUX)
GRANT ALL PRIVILEGES ON ideo_lab_db.* TO 'dev'@'%';

-- Retirer des droits
REVOKE DELETE ON ideo_lab_db.articles FROM 'lecteur'@'localhost';
TCL (Transaction Control)

Assure l'atomicité (Tout ou Rien) (ACID).

-- (Ex: Virement bancaire)
BEGIN;

-- 1. Débiter Alice
UPDATE comptes SET solde = solde - 100 WHERE id = 1;

-- 2. Créditer Bob
UPDATE comptes SET solde = solde + 100 WHERE id = 2;

-- (Si Erreur -> Annuler)
-- ROLLBACK;

-- (Si OK -> Valider)
COMMIT;
5.2 Index (Optimisation)
Concept (CREATE INDEX)

Un Index est une "table des matiÚres" (souvent un Arbre-B) pour une colonne. Il rend les SELECT (WHERE, JOIN) massivement plus rapides, mais ralentit (légÚrement) les INSERT/UPDATE.

Quand indexer ?

  • ClĂ©s Primaires (PK) (Auto-indexĂ©)
  • ClĂ©s ÉtrangĂšres (FK) (Crucial pour les JOIN)
  • Colonnes utilisĂ©es dans WHERE (ex: email)
  • Colonnes utilisĂ©es dans ORDER BY

Création (Syntaxe)
-- Index simple
CREATE INDEX idx_articles_status
ON articles (status);

-- Index unique (pour 'email')
CREATE UNIQUE INDEX idx_users_email
ON utilisateurs (email);

-- Index composé (pour WHERE status = ? AND date > ?)
CREATE INDEX idx_articles_status_date
ON articles (status, date_pub DESC);
5.3 EXPLAIN (Analyse de performance)

EXPLAIN (ou EXPLAIN ANALYZE) est la commande la plus importante pour l'optimisation. Elle vous montre le "Plan d'ExĂ©cution" (comment la BDD *exĂ©cute* votre requĂȘte).

Exemple (EXPLAIN)
-- La requĂȘte
EXPLAIN SELECT * FROM utilisateurs WHERE email = 'alice@mail.com';
Résultat (Mauvais - Pas d'index sur 'email')
QUERY PLAN
--------------------------------------------------------------
Seq Scan on utilisateurs  (cost=0.00..1500.00 rows=1)
  Filter: (email = 'alice@mail.com'::text)
(2 rows)

-- (Seq Scan = Full Table Scan. Lit 1500 pages.)
Résultat (Bon - AprÚs CREATE INDEX idx_users_email...)
QUERY PLAN
--------------------------------------------------------------
Index Scan using idx_users_email on utilisateurs  (cost=0.10..8.12 rows=1)
  Index Cond: (email = 'alice@mail.com'::text)
(2 rows)

-- (Index Scan. Rapide.)
6.1 Cheat-sheet SQL (DQL/DML)
DML (Données)
-- Create
INSERT INTO table (col1, col2) VALUES (val1, val2);

-- Read
SELECT * FROM table;

-- Update
UPDATE table SET col1 = val1 WHERE id = 1;

-- Delete
DELETE FROM table WHERE id = 1;
DQL (RequĂȘte)
SELECT
    t1.col1,
    COUNT(t2.id) AS total
FROM
    table1 t1
INNER JOIN
    table2 t2 ON t1.id = t2.t1_id
WHERE
    t1.col1 LIKE 'A%'
    AND t1.date > '2024-01-01'
GROUP BY
    t1.col1
HAVING
    COUNT(t2.id) > 5
ORDER BY
    total DESC
LIMIT 10;