Chapitre 1112 minDébutant
Vues SQL : CREATE VIEW
Par Josselyn C., prof de BTS SIO. Mis à jour le .
À la fin de ce chapitre, tu sauras
- créer, remplacer et supprimer une vue ;
- simplifier des requêtes répétées grâce à une vue ;
- restreindre l'accès aux données avec une vue ;
- savoir quand une vue est modifiable.
3 exercices corrigés
Une vue SQL est une requête SELECT enregistrée dans la base sous un nom, avec CREATE VIEW. On l'interroge comme une table, mais elle ne stocke pas de données : chaque fois que tu l'utilises, le SGBD exécute la requête qui la définit, sur les données du moment. Les exemples créent des vues sur la base d'exemple parc ; le chapitre GRANT, REVOKE et rôles s'en sert ensuite pour restreindre l'accès.
Créer, remplacer, supprimer
CREATE VIEW v_ticket_detail AS
SELECT t.id_ticket, t.titre, t.priorite, t.statut, t.date_ouverture, t.date_cloture,
CONCAT(e.prenom, ' ', e.nom) AS demandeur,
s.libelle AS service,
p.num_inventaire
FROM ticket t
JOIN employe e ON e.id_employe = t.id_demandeur
JOIN service s ON s.id_service = e.id_service
LEFT JOIN poste p ON p.id_poste = t.id_poste;
La vue s'utilise ensuite comme une table, avec ses propres filtres :
SELECT id_ticket, titre, demandeur
FROM v_ticket_detail
WHERE statut <> 'resolu' AND service = 'Logistique'
ORDER BY id_ticket;
| id_ticket | titre | demandeur |
|---|---|---|
| 6 | Ventilateur bruyant | Lucas Girard |
| 12 | Clavier cassé | Léa Rousseau |
| 13 | Lenteurs réseau à l'entrepôt | Nadia Faure |
Pour la modifier, CREATE OR REPLACE VIEW v_ticket_detail AS ... (ou ALTER VIEW) remplace sa définition. DROP VIEW IF EXISTS v_ticket_detail; la supprime, sans toucher aux tables. SHOW CREATE VIEW v_ticket_detail; affiche sa définition, et SHOW FULL TABLES; distingue les vues (VIEW) des tables (BASE TABLE).
Simplifier les requêtes
Une vue cache la complexité : les jointures, les calculs et les agrégats sont écrits une fois, testés une fois, et chacun s'en sert sans les réécrire. La charge de travail de chaque technicien :
CREATE VIEW v_charge_technicien AS
SELECT e.id_employe,
CONCAT(e.prenom, ' ', e.nom) AS technicien,
COUNT(*) AS nb_interventions,
SUM(i.duree_min) AS minutes
FROM employe e
JOIN intervention i ON i.id_technicien = e.id_employe
GROUP BY e.id_employe, e.prenom, e.nom;
SELECT technicien, nb_interventions, minutes
FROM v_charge_technicien
ORDER BY minutes DESC;
| technicien | nb_interventions | minutes |
|---|---|---|
| Antoine Mercier | 4 | 510 |
| Moussa Diallo | 7 | 450 |
| Chloé Lambert | 6 | 295 |
Un tableau de bord, une application ou un outil de reporting lisent la vue ; si la règle de calcul change, tu modifies la vue et tous en profitent.
Restreindre l'accès
Une vue montre certaines colonnes et certaines lignes. L'annuaire interne affiche le nom, l'email, la ligne directe et le service de chacun, mais il n'a pas à montrer les dates d'embauche ni la hiérarchie ; si la table reçoit l'IBAN chiffré des salariés, l'annuaire ne doit jamais l'exposer :
CREATE VIEW v_annuaire AS
SELECT e.nom, e.prenom, e.email, e.telephone, s.libelle AS service
FROM employe e
JOIN service s ON s.id_service = e.id_service;
Le responsable logistique ne doit voir que les tickets de son service :
CREATE VIEW v_tickets_logistique AS
SELECT t.id_ticket, t.titre, t.priorite, t.statut, t.date_ouverture
FROM ticket t
JOIN employe e ON e.id_employe = t.id_demandeur
JOIN service s ON s.id_service = e.id_service
WHERE s.code = 'LOGI';
Rassembler plusieurs sources avec UNION
C'est la vue que les sujets d'E7 SLAM donnent souvent à lire ou à écrire : elle met bout à bout des lignes venues de tables de structures différentes. Le journal d'activité des tickets réunit les ouvertures (table ticket) et les interventions (table intervention). Une ouverture n'a pas de durée : la colonne est remplacée par NULL, pour que les deux SELECT aient le même nombre de colonnes.
CREATE VIEW v_journal AS
SELECT t.id_ticket, t.date_ouverture AS moment, 'ouverture' AS evenement,
t.id_demandeur AS id_employe, NULL AS duree_min
FROM ticket t
UNION ALL
SELECT i.id_ticket, i.date_intervention, 'intervention', i.id_technicien, i.duree_min
FROM intervention i;
SELECT v.moment, v.evenement, e.nom, v.duree_min
FROM v_journal v
JOIN employe e ON e.id_employe = v.id_employe
WHERE v.id_ticket = 9
ORDER BY v.moment;
| moment | evenement | nom | duree_min |
|---|---|---|---|
| 2026-10-05 08:05:00 | ouverture | Faure | NULL |
| 2026-10-05 08:20:00 | intervention | Mercier | 240 |
| 2026-10-05 13:30:00 | intervention | Diallo | 180 |
| 2026-10-06 09:00:00 | intervention | Lambert | 150 |
Les noms des colonnes de la vue viennent du premier SELECT, d'où les alias écrits là. La vue compte 31 lignes (14 ouvertures et 17 interventions), et elle n'est pas modifiable : un UPDATE ne saurait pas dans quelle table écrire.
Vues modifiables et WITH CHECK OPTION
On peut faire un UPDATE, un INSERT ou un DELETE à travers une vue, à condition que chaque ligne de la vue corresponde à une seule ligne d'une table. Une vue n'est pas modifiable si elle contient un agrégat ou une fonction de fenêtrage, DISTINCT, GROUP BY, HAVING, UNION, une sous-requête dans le SELECT, ou une jointure externe. Avec une jointure interne, on ne peut modifier qu'une table à la fois. Et une colonne calculée, comme demandeur dans v_ticket_detail, ne se modifie jamais.
Une vue sur les tickets en attente :
CREATE VIEW v_tickets_en_attente AS
SELECT id_ticket, titre, priorite, statut, date_cloture
FROM ticket
WHERE statut <> 'resolu'
WITH CHECK OPTION;
-- Accepté : le ticket 12 reste en attente
UPDATE v_tickets_en_attente SET priorite = 'normale' WHERE id_ticket = 12;
-- Refusé : la ligne sortirait de la vue
UPDATE v_tickets_en_attente
SET statut = 'resolu', date_cloture = '2026-10-28 11:00:00'
WHERE id_ticket = 12;
ERROR 1369 (HY000): CHECK OPTION failed 'parc.v_tickets_en_attente'
WITH CHECK OPTION interdit toute modification qui ferait sortir une ligne de la vue. Sans elle, le second UPDATE réussirait et le ticket disparaîtrait de la vue de celui qui vient de le modifier. Quand une vue repose sur une autre vue, WITH CASCADED CHECK OPTION (le comportement par défaut) vérifie aussi les conditions des vues sous-jacentes ; WITH LOCAL CHECK OPTION ne vérifie que la vue elle-même.
Limites
- Pas plus rapide. Une vue ne stocke rien : la requête sous-jacente s'exécute à chaque appel. MySQL fusionne si possible la vue dans ta requête (algorithme
MERGE) ; sinon, il calcule son résultat dans une table temporaire (TEMPTABLE), par exemple pour une vue avecGROUP BY. - Définition figée. Un
SELECT *dans une vue est développé à la création : une colonne ajoutée ensuite à la table n'apparaît pas, et une colonne supprimée rend la vue inutilisable. Nomme les colonnes. - Pas de paramètre. Une vue ne prend pas d'argument : le filtre se fait dans la requête qui l'utilise.
- Vues sur vues. Empiler les vues rend les performances et les erreurs difficiles à comprendre. Deux niveaux, c'est déjà beaucoup.
- Pas de vue matérialisée en MySQL ou MariaDB. PostgreSQL et Oracle proposent
CREATE MATERIALIZED VIEW, qui stocke le résultat et le rafraîchit à la demande ; en MySQL, on l'imite avec une table remplie par un évènement planifié ou des triggers.
Exercices
Exercice 1 sur 3 La vue du parc Réussi À revoir
Crée une vue v_parc qui affiche pour chaque poste, y compris ceux en stock : le numéro d'inventaire, le type, la marque, le modèle, le prénom et le nom de l'utilisateur dans une colonne utilisateur (en stock sinon) et le libellé de son service dans une colonne service.
Voir la réponseMasquer la réponse de l'exercice 1
CREATE VIEW v_parc AS
SELECT p.num_inventaire, p.type_poste, p.marque, p.modele,
COALESCE(CONCAT(e.prenom, ' ', e.nom), 'en stock') AS utilisateur,
s.libelle AS service
FROM poste p
LEFT JOIN employe e ON e.id_employe = p.id_employe
LEFT JOIN service s ON s.id_service = e.id_service;
Le second LEFT JOIN est indispensable : avec un JOIN, les postes en stock, qui n'ont pas d'employé donc pas de service, disparaîtraient.
Tu avais trouvé ?
Exercice 2 sur 3 Utiliser la vue Réussi À revoir
À partir de v_parc seulement, affiche le nombre de postes par service, avec une ligne stock pour les postes non affectés, du plus grand nombre au plus petit.
Voir la réponseMasquer la réponse de l'exercice 2
SELECT COALESCE(service, 'stock') AS service, COUNT(*) AS nb_postes
FROM v_parc
GROUP BY service
ORDER BY nb_postes DESC, service;
| service | nb_postes |
|---|---|
| Commercial | 4 |
| Informatique | 3 |
| Logistique | 3 |
| Comptabilité | 2 |
| stock | 2 |
| Direction | 1 |
GROUP BY service porte sur la colonne de la vue : les deux postes en stock, dont le service vaut NULL, forment un groupe. MySQL accepte l'alias service dans l'ORDER BY ; il désigne ici la colonne calculée par COALESCE.
Tu avais trouvé ?
Exercice 3 sur 3 Modifiable ou non Réussi À revoir
Pour chacune des vues v_ticket_detail, v_charge_technicien et v_tickets_en_attente, dis si l'instruction suivante est acceptée, et pourquoi.
UPDATE v_ticket_detail SET priorite = 'haute' WHERE id_ticket = 14;UPDATE v_ticket_detail SET demandeur = 'H. Garnier' WHERE id_ticket = 14;UPDATE v_charge_technicien SET minutes = 0 WHERE id_employe = 11;DELETE FROM v_tickets_en_attente WHERE id_ticket = 14;
Voir la réponseMasquer la réponse de l'exercice 3
- Refusée.
v_ticket_detailcontient unLEFT JOINversposte: une jointure externe rend la vue non modifiable. Avec unJOINinterne à cet endroit, l'instruction serait acceptée, carprioritevient de la seule tableticket. - Refusée dans tous les cas :
demandeurest calculée parCONCAT, elle ne correspond à aucune colonne d'une table. - Refusée : la vue contient
GROUP BYet des agrégats. Une ligne de la vue résume plusieurs interventions ; quelle ligne modifier ? - Acceptée : la vue porte sur une seule table, sans agrégat. Le ticket 14 est supprimé de la table
ticket, et ses interventions éventuelles avec lui (clé étrangère enCASCADE).
Tu avais trouvé ?