Version 15 minutes :

Version 1 heure :

Le language SQL (Structured Query Language) permet d'effectuer des requêtes à une base de données relationnelle.

Découvrez comment tout cela fonctionne, apprenez à maîtriser vos données et à utiliser l'IA pour gagner du temps lors de la rédaction de requêtes.

Base de données relationnelle

Une base de données permet comme son nom l'indique de gérer des données. Cela peut servir à les analyser dans le cadre de la "Date Science" ou tout simplement à les utiliser dans son application afin de la rendre dynamique. La majorité des applications que tu utilises tous les jours reposent sur ce système.

Dans une base de données relationnelle on organise la donnée sous forme de tableaux qui représentent des entités.

SQL - Bases de données relationnelles

Entités

Si par exemple je tente de refaire la base de données de ChatGPT, je pourrais avoir des utilisateurs qui ont des conversations qui elles mêmes contiennent des messages, ce qui pourrait ressembler à ça :

Entités base de données ChatGPT simple

Mais avec la version entreprise, je pourrais me retrouver avec des entreprises qui ont des utilisateurs qui ont des conversations mais aussi des rôles tout en conservant les messages...

Entités base de données ChatGPT avec entreprise (complexe)

Tableaux

Et bien en base de données (en retirant les rôles pour simplifier) cela pourrait ressembler à ça :

Tableaux- Base de données relationnelle

Chaque entité possède son tableau et tous les éléments d'une même entité sont dans le même tableau.

Relations et clés

Pour lier un élément à un autre et ainsi créer une "relation", on créer une colonne qui fait référence à une ligne dans un autre tableau. Pour nos conversations et messages, nos tableaux pourraient ressembler à ça :

Tableaux relationnels conversations et messages (SQL)

Clé primaire

Ici, toutes nos entités ont un identifiant unique (ici en rouge), c'est ce que l'on appelle la clé primaire car c'est elle qui permet de faire la différence entre toutes les lignes d'un même tableau.

Clé étrangère

Il existe aussi des clés étrangères (ici en bleu) qui permettent de créer une relation en l'occurence "One to Many" car une conversation peut avoir plusieurs messages. Un clé étrangère fait tout simplement référence à une ligne dans un autre tableau.

Clé unique

Pour finir sur les clés, on peut avoir un élément d'une colonne que l'on veut unique, par exemple si l'on ne veut pas que plusieurs utilisateurs puissent avoir la même adresse mail, on la rend unique (ci dessous en vert).

Relation many to many dans SQL avec entreprises (structures) et utilisateurs.

Ici, on remarque que j'ai ajouté un troisième tableau dans le but de créer une relation que l'on appelle "Many to Many". Cela signifie que plusieurs utilisateurs peuvent appartenir à plusieurs entreprises, c'est possible par exemple dans le cas où un entrepreneur gère plusieurs structures : ici l'utilisateur 1 ("Elon") appartient à la fois à l'entreprise 1 ("Meta") et à l'entreprise 2 ("Amazon").

Les types de données

Chaque colonne possède un type pour garder de la cohérence et d’éviter certaines erreurs.

Types de données SQLITE pour une todo list

Un id c’est quasiment toujours un nombre entier qu’on appelle INT ou INTEGER et par exemple dans une application de Todo-List on pourrait avoir un BOOL (ou booléen) qui vaut 1 (true; vrai) ou 0 (false, faux) en fonction de si la tâche est effectuée ou non et un texte de type TEXT qui permet de décrire la tâche à faire.

Voici un tableau des types de données principaux :

Types de données principaux SQL, MySQL, SQLite, PostgreSQL

La pratique

On va créer à l'aide de l'IA une base de données basique pour un site E-Commerce avec donc des clients, des produits et des commandes.

SQLite Online

Pour gérer nos données et exécuter du SQL nous avons besoin d'un SGBDR (Système de Gestion de Base de Données Relationnelle). Il en existe beaucoup comme PostgreSQL ou encore MySQL mais le plus simple pour commencer est SQLite car il ne nécessite pas d'installation et enregistre tout dans un fichier.

L'application web SQLite Online nous permet de faire tout cela très simplement dans une interface prête à l'emploi.

La première chose que je fais quand j'arrive dessus est de supprimer la table de demo, pour cela il suffit d'exécuter la requête suivante :

DROP TABLE demo

Pour exécuter la requête, il suffit de faire Shift + Entrée.

La table a donc normalement disparu.

Créer les tables avec ClaudeAI ou ChatGPT

Voici la demande que j'ai formulée :

Crée les schémas pour une base SQLite d'un site E-Commerce basique avec une table produit, une table clients et une table commandes.

ClaudeAI m'a alors généré ce schéma :

Schéma typé base de données relationnelle SQL site E-Commerce basique

Accompagné de cette requête SQL complète permettant de créer les tableaux dans ma base de données :

-- Table des clients
CREATE TABLE clients (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    nom VARCHAR(100) NOT NULL,
    prenom VARCHAR(100) NOT NULL,
    email VARCHAR(255) NOT NULL UNIQUE,
    adresse TEXT NOT NULL,
    telephone VARCHAR(20),
    date_inscription DATETIME DEFAULT CURRENT_TIMESTAMP,
    CONSTRAINT email_valide CHECK (email LIKE '%@%.%')
);

-- Table des produits
CREATE TABLE produits (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    nom VARCHAR(200) NOT NULL,
    description TEXT,
    prix DECIMAL(10,2) NOT NULL,
    stock INTEGER NOT NULL DEFAULT 0,
    categorie VARCHAR(100),
    date_creation DATETIME DEFAULT CURRENT_TIMESTAMP,
    CONSTRAINT prix_positif CHECK (prix > 0),
    CONSTRAINT stock_positif CHECK (stock >= 0)
);

-- Table des commandes
CREATE TABLE commandes (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    client_id INTEGER NOT NULL,
    produit_id INTEGER NOT NULL,
    quantite INTEGER NOT NULL,
    prix_total DECIMAL(10,2) NOT NULL,
    statut VARCHAR(50) DEFAULT 'en_attente',
    date_commande DATETIME DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (client_id) REFERENCES clients(id),
    FOREIGN KEY (produit_id) REFERENCES produits(id),
    CONSTRAINT quantite_positive CHECK (quantite > 0),
    CONSTRAINT prix_total_positif CHECK (prix_total >= 0)
);

-- Index pour améliorer les performances
CREATE INDEX idx_commandes_client ON commandes(client_id);
CREATE INDEX idx_commandes_produit ON commandes(produit_id);
CREATE INDEX idx_produits_categorie ON produits(categorie);

Claude a notamment inclu :

  • Des commentaires (avec --)
  • Des contraintes d'intégrité (CHECK, FOREIGN KEY)
  • Des index pour optimiser les performances
  • Des contraintes d'unicité (email : clé unique)

Pour comprendre les choses plus simplement avec ma Todo Liste, la création de la table ressemblerait à ça :

CREATE TABLE todos (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    text TEXT NOT NULL,
    done BOOL NOT NULL DEFAULT 0
  );

Je dis donc que je veux créer ma table avec son nom, puis, entre parenthèses, je liste les colonnes de mon tableau avec leurs propriétés. L'id par exemple est un INTEGER donc un nombre entier et c'est la clé primaire.

L'autoincrement signifie que c'est un nombre qui commence à 1 et au fur et à mesure que l'on ajoute des lignes il s'incrémente, sur la deuxième ligne on aura 2, la troisième 3 et ainsi de suite. À noter que le compteur ne s'arrête pas, ne change pas et ne repars pas en arrière. Si par exemple je supprimer la ligne 2 il n'y aura plus jamais de ligne avec l'id 2 toujours dans un but de conserver de la cohérence et de l'intégrité dans les données.

Pour la suite je vous recommande d'exécuter la requête SQL complète fournie pour Claude, pour cela vous pouvez faire copier / coller puis Shift+Entrée.

SQL Online tables et SQL

J'ai donc bien mes tables qui ont été créées comme je peux le voir dans la colonne de gauche.

Données d'exemple

Afin de remplir ma base de données pour pouvoir faire des requêtes intéressantes, je vais demander à Claude de m'ajouter des données d'exemple.

Insère des données d'exemple stp

Claude me sort alors ça :

-- Insertion de clients
INSERT INTO clients (nom, prenom, email, adresse, telephone) VALUES
    ('Dubois', 'Marie', 'marie.dubois@email.com', '15 rue des Lilas, 75001 Paris', '0612345678'),
    ('Martin', 'Jean', 'jean.martin@email.com', '28 avenue Victor Hugo, 69002 Lyon', '0623456789'),
    ('Bernard', 'Sophie', 'sophie.bernard@email.com', '7 rue de la République, 13001 Marseille', '0634567890'),
    ('Petit', 'Pierre', 'pierre.petit@email.com', '45 boulevard Voltaire, 44000 Nantes', '0645678901'),
    ('Robert', 'Claire', 'claire.robert@email.com', '12 rue du Commerce, 31000 Toulouse', '0656789012');

-- Insertion de produits
INSERT INTO produits (nom, description, prix, stock, categorie) VALUES
    ('Smartphone XZ Pro', 'Smartphone dernière génération, écran 6.5 pouces, 256GB', 899.99, 50, 'Électronique'),
    ('Laptop Performance', 'Ordinateur portable 15", i7, 16GB RAM, 512GB SSD', 1299.99, 30, 'Électronique'),
    ('Casque Audio Premium', 'Casque bluetooth avec réduction de bruit active', 249.99, 100, 'Audio'),
    ('Montre Connectée Sport', 'Montre connectée avec GPS et moniteur cardiaque', 199.99, 75, 'Accessoires'),
    ('Enceinte Bluetooth', 'Enceinte portable waterproof, autonomie 20h', 129.99, 60, 'Audio'),
    ('Tablette Pro', 'Tablette 11", 128GB, WiFi + 5G', 699.99, 40, 'Électronique'),
    ('Écouteurs Sans Fil', 'Écouteurs true wireless, autonomie 30h', 159.99, 120, 'Audio');

-- Insertion de commandes
INSERT INTO commandes (client_id, produit_id, quantite, prix_total, statut) VALUES
    (1, 1, 1, 899.99, 'livré'),
    (1, 3, 1, 249.99, 'en_cours'),
    (2, 2, 1, 1299.99, 'livré'),
    (3, 4, 2, 399.98, 'en_attente'),
    (4, 5, 1, 129.99, 'en_cours'),
    (5, 6, 1, 699.99, 'livré'),
    (2, 7, 2, 319.98, 'livré'),
    (3, 1, 1, 899.99, 'en_attente'),
    (4, 3, 1, 249.99, 'livré'),
    (5, 2, 1, 1299.99, 'en_cours');

Il me propose donc d'insérer 5 clients, 7 produits et 10 commandes, c'est parfait !

Je copie / colle cette requête et l'exécute dans ma base de données SQLite.

Sélection (SELECT FROM)

Pour voir le résultat, je vais lister le contenu du tableau des contacts.

J'exécute ceci :

SELECT * FROM clients

Cette requête sélectionne toutes les lignes et colonnes de la table clients et j'obtiens bien ceci !

Table clients exemple SQL

Je peux également sélectionner seulement les colonnes nom et prénom pour toutes les lignes en remplaçant l'étoile par les noms des champs que je souhaite :

SELECT nom, prenom FROM clients
Table clients exemple SQL, seulement nom prenom

Ou même ne sélectionner qu'une seule ligne, par exemple en prenant le prénom et le numéro de téléphone pour le client dont l'id est égal à 5.

SELECT prenom, telephone FROM clients WHERE id = '5';
Table clients exemple SQL, seulement prenom et tel, ligne 5

Vous venez d'apprendre à faire une requête de sélection pour récupérer des données dans une base de données, félicitations !

Insertion d'un client (INSERT INTO)

Maintenant, nous allons insérer une nouvelle ligne dans notre base de données.

Comme pour nos données d'exemples, j'utilise INSERT INTO :

INSERT INTO clients (nom, prenom, email, adresse, telephone, date_inscription) VALUES ('Monnier', 'Timothée', 'contact@timotion.fr', 'rue du coucou', '0686060606', 'ok');

Pour regarder le résultat je fais donc une sélection :

SELECT nom, prenom FROM clients ORDER BY id DESC

Ici je demande donc le nom et le prénom dans la table de clients et je rajoute de quoi ordonner la lise par ID décroissant ce qui en tout logique devrais mettre mon nom et mon prénom en premier :

Table clients exemple SQL, seulement nom et prenom par ordre d'id décroissant

Bingo !

Suppression d'un client (DELETE FROM WHERE)

J'ai ajouté un client, je vous montre maintenant comment le retirer !

Pour ça je peux simplement dire que je supprime dans la table clients toutes les lignes dont le prénom vaut Timothée :

DELETE FROM clients WHERE prenom = 'Timothée'

Notez que le plus courant et sécurisé est de le faire avec l'id :

DELETE FROM clients WHERE id = '6'

Ça évite si 2 clients ont le même prénom de supprimer les 2 puisque l'id est une clé primaire et donc unique.

Mise à jour d'un client (UPDATE SET WHERE)

J'aimerais maintenant que Marie, la première cliente importée par Claude donc qui possède l'id 1 change de numéro de téléphone en passant de 0612345678 à 0606060606et de nom de famille en passant de Dubois à Petit.

Pour ce faire, j'utilise une requête UPDATE:

UPDATE clients SET nom = 'Petit', telephone = '0606060606' WHERE id = '1';

Si j'affiche à nouveau ma Liste complète :

SELECT * FROM clients
Table clients exemple SQL complète avec le nom et le tel changés.

Mon premier contact s'appelle effectivement maintenant Marie Petit avec le numéro 0606060606.

Jointure commande / client (UPDATE SET WHERE)

Un jointure ça sert à croiser les informations de plusieurs tableaux, ça peut paraître complexe mais en vérité c'est très simple.

Imaginons que je souhaite récupérer la liste des commandes avec à chaque fois l'id, le statut et le prénom de la personne qui a effectué la commande. Je n'ai pas tout dans une seul tableau puisque dans le tableau des commandes, la seule chose que je connais sur le client c'est son id dans la colonne client_id.

En faisant une jointure SQL, ce que je dis au système qui gère ma base de données c'est :

Ok, récupères moi toutes les commandes et pour chaque commande tu regardes le prénom du client dont l'id est client_id dans la table clients.

En SQL ça donne ça :

SELECT commandes.id, commandes.statut, clients.prenom FROM commandes INNER Join clients ON commandes.client_id = clients.id;

Je veux donc l'id et le statut dans la table commandes et le prénom dans la table clients. Pour ça l'indication que je donne est que client_id dans commandes a la même valeur que l'id dans la table clients et le tour est joué !

Table commandes exemple SQL jointe à la table de clients pour récupérer le prénom.

Comme on peut le voir les deux premières commandes ont été effectuées par la même cliente, j'ai donc Marie qui s'affiche 2 fois.

Requêtes plus complexes

Ce que je ne vous ai pas dit c'est que Claude, en plus de m'avoir proposé des données d'exemple m'a aussi fourni des requêtes intéressantes à tester. L'objectif n'est pas que vous sachiez tout faire, mais que vous sachiez ce qu'il est possible de faire. Maintenant que les IA sont là, il est inutile de tout connaître par coeur mais important de connaître les bases, de comprendre et de savoir où et comment trouver les informations.

Top des produits les plus commandés

SELECT p.nom, SUM(c.quantite) as total_vendu
FROM produits p
JOIN commandes c ON p.id = c.produit_id
GROUP BY p.nom
ORDER BY total_vendu DESC;

Ici, on sélectionne les produits et on fait la somme des quantités dans les commandes, on les regroupe ensuite par noms et on ordonne cela par ordre décroissant de total vendu.

Table exemple SQL total des produits les plus commandés (group by et sum)

Plusieurs choses nouvelles à noter ici :

  1. L'usage d'alias sur la table, ici après le nom d'une table, Claude à ajouté une lettre (produits p et commandes c). Cela permet de rendre plus concise la requête en évitant d'écrire le nom de la table complète quand on y fait allusion. Par exemple après le SELECT plutôt que d'écrire produits.nom, Claude a pu écrire p.nom.
  2. L'usage d'alias sur une colonne. Celui-ci est intéressant car il permet de renommer une colonne, ici, au lieu de s'appeler littéralement SUM(c.quantite), la colonne qui contient la somme des quantités s'appelle total_vendu car elle a été renommée. C'est ensuite plus facile de l'utiliser lors du tri décroissant et aussi plus lisible à l'affichage dans la réponse.
  3. L'usage d'une somme justement, ça permet de prendre toutes les quantités renvoyées et de les additionner.
  4. Toujours en rapport avec cette somme, le GROUP BY permet de faire une somme différente pour chaque nom de produit, sinon nous n'aurions qu'une somme totale avec le total des produits vendus au lieu d'un somme par produits.

Chiffre d'affaires par client

SELECT cl.nom, cl.prenom, SUM(c.prix_total) as total_achats
FROM clients cl
JOIN commandes c ON cl.id = c.client_id
GROUP BY cl.id
ORDER BY total_achats DESC;

Ici on calcule le chiffre d'affaire pour chaque client en affichant en premier le plus important. C'est un peu le même principe que la requête précédente sauf qu'au lieu d'additionner les quantités par nom de produit, on additionne les prix totaux par clients.

Table exemple SQL : chiffre d'affaires par client (group by et sum)

Statut des commandes

SELECT statut, COUNT(*) as nombre
FROM commandes
GROUP BY statut;

Finalement dans cette dernière requête, Claude nous propose quelque-chose qui pourrait être intéressant à afficher sur un tableau de bord pour suivre l'avancée des statuts des commandes.

On a donc 2 commandes en attente, 3 en cours et 5 livrées.

La nouveauté ici est le COUNT qui associé à un GROUP BY sur le statut permet de compter l'occurence de chaque statut dans la liste des commandes. Si je n'avais pas mis le GROUP BY, je n'aurais eu qu'un seul nombre, les total de tous les statuts, soit le nombre de commandes ou le nombre de lignes dans ma table tout simplement.

Merci pour votre lecture !

Pour aller plus loin

Apprendre SQL : Bases de données relationnelles