đïž SQL â Le Langage de Base de DonnĂ©es (DDL, DML, DQL)
Guide complet IDEOâLab : SELECT, JOIN, GROUP BY, Subqueries, UNION & EXPLAIN.
Vue d'ensemble
SQL (Structured Query Language), SGBDR, DDL, DML, DQL.
SQL SGBDRDDL (Data Definition)
CREATE, ALTER, DROP (Tables, DBs).
Contraintes (DDL)
PRIMARY KEY, FOREIGN KEY, UNIQUE, NOT NULL.
DML (INSERT, UPDATE, DELETE)
Commandes de manipulation (Create, Update, Delete).
INSERT UPDATE DELETEDQL : SELECT (Read)
SELECT, FROM, *, AS (Alias).
Clauses WHERE (Filtres)
AND, OR, IN, BETWEEN, LIKE, IS NULL.
Tri & Limite
ORDER BY (ASC/DESC), LIMIT, OFFSET.
Agrégats & GROUP BY
COUNT(), SUM(), AVG(), GROUP BY. La base de l'analyse.
Clause HAVING
Filtrer *aprĂšs* un GROUP BY. (WHERE filtre *avant*).
Jointures (JOIN)
INNER JOIN, LEFT/RIGHT JOIN, FULL OUTER JOIN.
Opérateurs (UNION)
UNION (Combine les résultats, supprime doublons), UNION ALL (garde doublons).
Sous-RequĂȘtes (ImbriquĂ©es)
IN, EXISTS, (SELECT ... FROM (SELECT ...)).
CTEs (WITH)
Common Table Expressions (WITH ... AS ...). Plus lisible.
Fonctions FenĂȘtrĂ©es
ROW_NUMBER(), PARTITION BY, OVER().
DCL & TCL
GRANT, REVOKE (DCL). COMMIT, ROLLBACK (TCL).
Index (CREATE INDEX)
CREATE INDEX, B-Tree, Index Composé.
EXPLAIN (Optimisation)
EXPLAIN, Full Table Scan vs Index Scan.
Cheat-sheet SQL
Commandes DQL (SELECT) & DML (INSERT...).
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
| Sigle | Nom | Commandes | But |
|---|---|---|---|
| DDL | Data Definition Language | CREATE, ALTER, DROP | Définir la structure (tables, index). |
| DML | Data Manipulation Language | INSERT, UPDATE, DELETE | Manipuler les données (lignes). |
| DQL | Data Query Language | SELECT | Interroger/Lire les données. |
| DCL | Data Control Language | GRANT, REVOKE | Gérer les permissions. |
| TCL | Transaction Control | COMMIT, ROLLBACK | Gé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 | +----+---------+------------+
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).
| SGBDR | Dialecte (Langage de script) | Exemple de Fonction |
|---|---|---|
| PostgreSQL | PL/pgSQL | NOW(), AGE(), ::date (cast) |
| MySQL/MariaDB | (Procédural) | NOW(), DATE_FORMAT(), LIMIT |
| Microsoft SQL Server | T-SQL (Transact-SQL) | GETDATE(), ISNULL(), TOP 10 |
| Oracle | PL/SQL | SYSDATE, NVL(), ROWNUM <= 10 |
Ce guide se concentre sur le SQL ANSI (PostgreSQL/MariaDB).
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;
Les contraintes garantissent l'intégrité des données.
| Contrainte | Description |
|---|---|
PRIMARY KEY | Identifiant unique (non-nul) de la ligne. (Indexé auto). |
FOREIGN KEY | Garantit que la valeur existe dans une autre table (intégrité référentielle). |
UNIQUE | La valeur doit ĂȘtre unique dans la colonne (ex: email). |
NOT NULL | La colonne ne peut pas ĂȘtre vide (NULL). |
CHECK | Condition 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)
);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';
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)
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'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;
GROUP BYFonctions 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.00HAVING (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)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
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)
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;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;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 | 2DCL (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;
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);
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.)
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;