Les vues sont des objets de la base de données, constitués d'un nom, et d'une requête de sélection.
Une fois qu'une vue est définie, on peut l'utiliser comme on le ferait avec une table ; table qui serait constituée des données sélectionnées par la requête définissant la vue.
Nous verrons dans ce chapitre :
- comment créer, modifier, supprimer une vue ;
- à quoi peut servir une vue ;
- deux algorithmes différents pouvant être utilisées par les vues ;
- comment modifier les données à partir d'une vue.
Etat actuel de la base de données
Note : les tables de test et les procédures stockées ne sont pas reprises.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 | SET NAMES utf8; DROP TABLE IF EXISTS Erreur; DROP TABLE IF EXISTS Animal_histo; DROP TABLE IF EXISTS Adoption; DROP TABLE IF EXISTS Animal; DROP TABLE IF EXISTS Race; DROP TABLE IF EXISTS Espece; DROP TABLE IF EXISTS Client; CREATE TABLE Client ( id smallint(5) unsigned NOT NULL AUTO_INCREMENT, nom varchar(100) NOT NULL, prenom varchar(60) NOT NULL, adresse varchar(200) DEFAULT NULL, code_postal varchar(6) DEFAULT NULL, ville varchar(60) DEFAULT NULL, pays varchar(60) DEFAULT NULL, email varbinary(100) DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY ind_uni_email (email) ) ENGINE=InnoDB AUTO_INCREMENT=17; LOCK TABLES Client WRITE; INSERT INTO Client VALUES (1,'Dupont','Jean','Rue du Centre, 5','45810','Houtsiplou','France','jean.dupont@email.com'),(2,'Boudur','Marie','Place de la Gare, 2','35840','Troudumonde','France','marie.boudur@email.com'),(3,'Trachon','Fleur','Rue haute, 54b','3250','Belville','Belgique','fleurtrachon@email.com'), (4,'Van Piperseel','Julien',NULL,NULL,NULL,NULL,'jeanvp@email.com'),(5,'Nouvel','Johan',NULL,NULL,NULL,'Suisse','johanetpirlouit@email.com'),(6,'Germain','Frank',NULL,NULL,NULL,NULL,'francoisgermain@email.com'), (7,'Antoine','Maximilien','Rue Moineau, 123','4580','Trocoul','Belgique','max.antoine@email.com'),(8,'Di Paolo','Hector',NULL,NULL,NULL,'Suisse','hectordipao@email.com'),(9,'Corduro','Anaelle',NULL,NULL,NULL,NULL,'ana.corduro@email.com'), (10,'Faluche','Eline','Avenue circulaire, 7','45870','Garduche','France','elinefaluche@email.com'),(11,'Penni','Carine','Boulevard Haussman, 85','1514','Plasse','Suisse','cpenni@email.com'),(12,'Broussaille','Virginie','Rue du Fleuve, 18','45810','Houtsiplou','France','vibrousaille@email.com'), (13,'Durant','Hannah','Rue des Pendus, 66','1514','Plasse','Suisse','hhdurant@email.com'),(14,'Delfour','Elodie','Rue de Flore, 1','3250','Belville','Belgique','e.delfour@email.com'),(15,'Kestau','Joel',NULL,NULL,NULL,NULL,'joel.kestau@email.com'); UNLOCK TABLES; CREATE TABLE Espece ( id smallint(6) unsigned NOT NULL AUTO_INCREMENT, nom_courant varchar(40) NOT NULL, nom_latin varchar(40) NOT NULL, description text, prix decimal(7,2) unsigned DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY nom_latin (nom_latin) ) ENGINE=InnoDB AUTO_INCREMENT=6; LOCK TABLES Espece WRITE; INSERT INTO Espece VALUES (1,'Chien','Canis canis','Bestiole à quatre pattes qui aime les caresses et tire souvent la langue',200.00),(2,'Chat','Felis silvestris','Bestiole à quatre pattes qui saute très haut et grimpe aux arbres',150.00),(3,'Tortue d''Hermann','Testudo hermanni','Bestiole avec une carapace très dure',140.00), (4,'Perroquet amazone','Alipiopsitta xanthops','Joli oiseau parleur vert et jaune',700.00),(5,'Rat brun','Rattus norvegicus','Petite bestiole avec de longues moustaches et une longue queue sans poils',10.00); UNLOCK TABLES; CREATE TABLE Race ( id smallint(6) unsigned NOT NULL AUTO_INCREMENT, nom varchar(40) NOT NULL, espece_id smallint(6) unsigned NOT NULL, description text, prix decimal(7,2) unsigned DEFAULT NULL, date_insertion datetime DEFAULT NULL, utilisateur_insertion varchar(20) DEFAULT NULL, date_modification datetime DEFAULT NULL, utilisateur_modification varchar(20) DEFAULT NULL, PRIMARY KEY (id) ) ENGINE=InnoDB AUTO_INCREMENT=11; LOCK TABLES Race WRITE; INSERT INTO Race VALUES (1,'Berger allemand',1,'Chien sportif et élégant au pelage dense, noir-marron-fauve, noir ou gris.',485.00,'2012-05-21 00:53:36','Test','2012-05-21 00:53:36','Test'),(2,'Berger blanc suisse',1,'Petit chien au corps compact, avec des pattes courtes mais bien proportionnées et au pelage tricolore ou bicolore.',935.00,'2012-05-21 00:53:36','Test','2012-05-21 00:53:36','Test'),(3,'Singapura',2,'Chat de petite taille aux grands yeux en amandes.',985.00,'2012-05-21 00:53:36','Test','2012-05-21 00:53:36','Test'), (4,'Bleu russe',2,'Chat aux yeux verts et à la robe épaisse et argentée.',835.00,'2012-05-21 00:53:36','Test','2012-05-21 00:53:36','Test'),(5,'Maine coon',2,'Chat de grande taille, à poils mi-longs.',735.00,'2012-05-21 00:53:36','Test','2012-05-21 00:53:36','Test'),(7,'Sphynx',2,'Chat sans poils.',1235.00,'2012-05-21 00:53:36','Test','2012-05-21 00:53:36','Test'), (8,'Nebelung',2,'Chat bleu russe, mais avec des poils longs...',985.00,'2012-05-21 00:53:36','Test','2012-05-21 00:53:36','Test'),(9,'Rottweiller',1,'Chien d''apparence solide, bien musclé, à la robe noire avec des taches feu bien délimitées.',630.00,'2012-05-21 00:53:36','Test','2012-05-22 00:54:13','sdz@localhost'),(10,'Yorkshire terrier',1,'Chien de petite taille au pelage long et soyeux de couleur bleu et feu.',700.00,'2012-05-22 00:58:25','sdz@localhost','2012-05-22 00:58:25','sdz@localhost'); UNLOCK TABLES; CREATE TABLE Animal ( id smallint(6) unsigned NOT NULL AUTO_INCREMENT, sexe char(1) DEFAULT NULL, date_naissance datetime NOT NULL, nom varchar(30) DEFAULT NULL, commentaires text, espece_id smallint(6) unsigned NOT NULL, race_id smallint(6) unsigned DEFAULT NULL, mere_id smallint(6) unsigned DEFAULT NULL, pere_id smallint(6) unsigned DEFAULT NULL, disponible tinyint(1) DEFAULT 1, PRIMARY KEY (id), UNIQUE KEY ind_uni_nom_espece_id (nom,espece_id) ) ENGINE=InnoDB AUTO_INCREMENT=76; LOCK TABLES Animal WRITE; INSERT INTO Animal VALUES (1,'M','2010-04-05 13:43:00','Rox','Mordille beaucoup',1,1,18,22,1),(2,NULL,'2010-03-24 02:23:00','Roucky',NULL,2,NULL,40,30,1),(3,'F','2010-09-13 15:02:00','Schtroumpfette',NULL,2,4,41,31,0), (4,'F','2009-08-03 05:12:00',NULL,'Bestiole avec une carapace très dure',3,NULL,NULL,NULL,1),(5,NULL,'2010-10-03 16:44:00','Choupi','Né sans oreille gauche',2,NULL,NULL,NULL,0),(6,'F','2009-06-13 08:17:00','Bobosse','Carapace bizarre',3,NULL,NULL,NULL,1), (7,'F','2008-12-06 05:18:00','Caroline',NULL,1,2,NULL,NULL,1),(8,'M','2008-09-11 15:38:00','Bagherra',NULL,2,5,NULL,NULL,0),(9,NULL,'2010-08-23 05:18:00',NULL,'Bestiole avec une carapace très dure',3,NULL,NULL,NULL,1), (10,'M','2010-07-21 15:41:00','Bobo','Petit pour son âge',1,NULL,7,21,1),(11,'F','2008-02-20 15:45:00','Canaille',NULL,1,NULL,NULL,NULL,1),(12,'F','2009-05-26 08:54:00','Cali',NULL,1,2,NULL,NULL,1), (13,'F','2007-04-24 12:54:00','Rouquine',NULL,1,1,NULL,NULL,1),(14,'F','2009-05-26 08:56:00','Fila',NULL,1,2,NULL,NULL,1),(15,'F','2008-02-20 15:47:00','Anya',NULL,1,NULL,NULL,NULL,0), (16,'F','2009-05-26 08:50:00','Louya',NULL,1,NULL,NULL,NULL,0),(17,'F','2008-03-10 13:45:00','Welva',NULL,1,NULL,NULL,NULL,1),(18,'F','2007-04-24 12:59:00','Zira',NULL,1,1,NULL,NULL,0), (19,'F','2009-05-26 09:02:00','Java',NULL,1,2,NULL,NULL,1),(20,NULL,'2007-04-24 12:45:00','Balou',NULL,1,1,NULL,NULL,1),(21,'F','2008-03-10 13:43:00','Pataude',NULL,1,NULL,NULL,NULL,0), (22,'M','2007-04-24 12:42:00','Bouli',NULL,1,1,NULL,NULL,1),(24,'M','2007-04-12 05:23:00','Cartouche',NULL,1,NULL,NULL,NULL,1),(25,'M','2006-05-14 15:50:00','Zambo',NULL,1,1,NULL,NULL,1), (26,'M','2006-05-14 15:48:00','Samba',NULL,1,1,NULL,NULL,0),(27,'M','2008-03-10 13:40:00','Moka',NULL,1,NULL,NULL,NULL,0),(28,'M','2006-05-14 15:40:00','Pilou',NULL,1,1,NULL,NULL,1), (29,'M','2009-05-14 06:30:00','Fiero',NULL,2,3,NULL,NULL,1),(30,'M','2007-03-12 12:05:00','Zonko',NULL,2,5,NULL,NULL,0),(31,'M','2008-02-20 15:45:00','Filou',NULL,2,4,NULL,NULL,1), (32,'M','2009-07-26 11:52:00','Spoutnik',NULL,3,NULL,52,NULL,0),(33,'M','2006-05-19 16:17:00','Caribou',NULL,2,4,NULL,NULL,1),(34,'M','2008-04-20 03:22:00','Capou',NULL,2,5,NULL,NULL,1), (35,'M','2006-05-19 16:56:00','Raccou','Pas de queue depuis la naissance',2,4,NULL,NULL,1),(36,'M','2009-05-14 06:42:00','Boucan',NULL,2,3,NULL,NULL,1),(37,'F','2006-05-19 16:06:00','Callune',NULL,2,8,NULL,NULL,1), (38,'F','2009-05-14 06:45:00','Boule',NULL,2,3,NULL,NULL,0),(39,'F','2008-04-20 03:26:00','Zara',NULL,2,5,NULL,NULL,0),(40,'F','2007-03-12 12:00:00','Milla',NULL,2,5,NULL,NULL,0), (41,'F','2006-05-19 15:59:00','Feta',NULL,2,4,NULL,NULL,0),(42,'F','2008-04-20 03:20:00','Bilba','Sourde de l''oreille droite à 80%',2,5,NULL,NULL,0),(43,'F','2007-03-12 11:54:00','Cracotte',NULL,2,5,NULL,NULL,1), (44,'F','2006-05-19 16:16:00','Cawette',NULL,2,8,NULL,NULL,1),(45,'F','2007-04-01 18:17:00','Nikki','Bestiole avec une carapace très dure',3,NULL,NULL,NULL,1),(46,'F','2009-03-24 08:23:00','Tortilla','Bestiole avec une carapace très dure',3,NULL,NULL,NULL,1), (48,'F','2006-03-15 14:56:00','Lulla','Bestiole avec une carapace très dure',3,NULL,NULL,NULL,1),(49,'F','2008-03-15 12:02:00','Dana','Bestiole avec une carapace très dure',3,NULL,NULL,NULL,0),(50,'F','2009-05-25 19:57:00','Cheli','Bestiole avec une carapace très dure',3,NULL,NULL,NULL,1), (51,'F','2007-04-01 03:54:00','Chicaca','Bestiole avec une carapace très dure',3,NULL,NULL,NULL,1),(52,'F','2006-03-15 14:26:00','Redbul','Insomniaque',3,NULL,NULL,NULL,1),(54,'M','2008-03-16 08:20:00','Bubulle','Bestiole avec une carapace très dure',3,NULL,NULL,NULL,1), (55,'M','2008-03-15 18:45:00','Relou','Surpoids',3,NULL,NULL,NULL,0),(56,'M','2009-05-25 18:54:00','Bulbizard','Bestiole avec une carapace très dure',3,NULL,NULL,NULL,1),(57,'M','2007-03-04 19:36:00','Safran','Coco veut un gâteau !',4,NULL,NULL,NULL,0), (58,'M','2008-02-20 02:50:00','Gingko','Coco veut un gâteau !',4,NULL,NULL,NULL,0),(59,'M','2009-03-26 08:28:00','Bavard','Coco veut un gâteau !',4,NULL,NULL,NULL,0),(60,'F','2009-03-26 07:55:00','Parlotte','Coco veut un gâteau !',4,NULL,NULL,NULL,1), (61,'M','2010-11-09 00:00:00','Yoda',NULL,2,5,NULL,NULL,1),(62,'M','2010-11-05 00:00:00','Pipo',NULL,1,9,NULL,NULL,0),(69,'F','2012-02-13 15:45:00','Baba',NULL,5,NULL,NULL,NULL,0), (70,'M','2012-02-13 15:48:00','Bibo','Agressif',5,NULL,72,73,1),(72,'F','2008-02-01 02:25:00','Momy',NULL,5,NULL,NULL,NULL,1),(73,'M','2007-03-11 12:45:00','Popi',NULL,5,NULL,NULL,NULL,1), (75,'F','2007-03-12 22:03:00','Mimi',NULL,5,NULL,NULL,NULL,0); UNLOCK TABLES; CREATE TABLE Adoption ( client_id smallint(5) unsigned NOT NULL, animal_id smallint(5) unsigned NOT NULL, date_reservation date NOT NULL, date_adoption date DEFAULT NULL, prix decimal(7,2) unsigned NOT NULL, paye tinyint(1) NOT NULL DEFAULT '0', PRIMARY KEY (client_id,animal_id), UNIQUE KEY ind_uni_animal_id (animal_id) ) ENGINE=InnoDB; LOCK TABLES Adoption WRITE; INSERT INTO Adoption VALUES (1,8,'2012-05-21',NULL,735.00,1),(1,39,'2008-08-17','2008-08-17',735.00,1),(1,40,'2008-08-17','2008-08-17',735.00,1), (2,3,'2011-03-12','2011-03-12',835.00,1),(2,18,'2008-06-04','2008-06-04',485.00,1),(3,27,'2009-11-17','2009-11-17',200.00,1), (4,26,'2007-02-21','2007-02-21',485.00,1),(4,41,'2007-02-21','2007-02-21',835.00,1),(5,21,'2009-03-08','2009-03-08',200.00,1), (6,16,'2010-01-27','2010-01-27',200.00,1),(7,5,'2011-04-05','2011-04-05',150.00,1),(8,42,'2008-08-16','2008-08-16',735.00,1), (9,38,'2007-02-11','2007-02-11',985.00,1),(9,55,'2011-02-13','2011-02-13',140.00,1),(9,59,'2012-05-22',NULL,700.00,0), (10,49,'2010-08-17','2010-08-17',140.00,0),(11,32,'2008-08-17','2010-03-09',140.00,1),(11,62,'2011-03-01','2011-03-01',630.00,0), (12,15,'2012-05-22',NULL,200.00,1),(12,69,'2007-09-20','2007-09-20',10.00,1),(12,75,'2012-05-21',NULL,10.00,0), (13,57,'2012-01-10','2012-01-10',700.00,1),(14,58,'2012-02-25','2012-02-25',700.00,1),(15,30,'2008-08-17','2008-08-17',735.00,1); UNLOCK TABLES; ALTER TABLE Race ADD CONSTRAINT fk_race_espece_id FOREIGN KEY (espece_id) REFERENCES Espece (id); ALTER TABLE Animal ADD CONSTRAINT fk_race_id FOREIGN KEY (race_id) REFERENCES Race (id); ALTER TABLE Animal ADD CONSTRAINT fk_mere_id FOREIGN KEY (mere_id) REFERENCES Animal (id); ALTER TABLE Animal ADD CONSTRAINT fk_pere_id FOREIGN KEY (pere_id) REFERENCES Animal (id); ALTER TABLE Animal ADD CONSTRAINT fk_espece_id FOREIGN KEY (espece_id) REFERENCES Espece (id); ALTER TABLE Adoption ADD CONSTRAINT fk_client_id FOREIGN KEY (client_id) REFERENCES Client (id); ALTER TABLE Adoption ADD CONSTRAINT fk_adoption_animal_id FOREIGN KEY (animal_id) REFERENCES Animal (id); CREATE TABLE Animal_histo ( id smallint(6) unsigned NOT NULL, sexe char(1) DEFAULT NULL, date_naissance datetime NOT NULL, nom varchar(30) DEFAULT NULL, commentaires text, espece_id smallint(6) unsigned NOT NULL, race_id smallint(6) unsigned DEFAULT NULL, mere_id smallint(6) unsigned DEFAULT NULL, pere_id smallint(6) unsigned DEFAULT NULL, disponible tinyint(1) DEFAULT 1, date_histo datetime NOT NULL, utilisateur_histo varchar(20) NOT NULL, evenement_histo char(6) NOT NULL, PRIMARY KEY (id,date_histo) ) ENGINE=InnoDB; LOCK TABLES Animal_histo WRITE; INSERT INTO Animal_histo VALUES (10,'M','2010-07-21 15:41:00','Bobo',NULL,1,NULL,7,21,1,'2012-05-22 01:00:34','sdz@localhost','UPDATE'),(47,'F','2009-03-26 01:24:00','Scroupy','Bestiole avec une carapace très dure',3,NULL,NULL,NULL,1,'2012-05-22 01:00:34','sdz@localhost','DELETE'); UNLOCK TABLES; CREATE TABLE Erreur ( id tinyint(3) unsigned NOT NULL AUTO_INCREMENT, erreur varchar(255) DEFAULT NULL, PRIMARY KEY (id), UNIQUE KEY erreur (erreur) ) ENGINE=InnoDB AUTO_INCREMENT=8; LOCK TABLES Erreur WRITE; INSERT INTO Erreur VALUES (5,'Erreur : date_adoption doit être >= à date_reservation.'),(3,'Erreur : paye doit valoir TRUE (1) ou FALSE (0).'),(1,'Erreur : sexe doit valoir \"M\", \"F\" ou NULL.'); UNLOCK TABLES; -- -------- -- -- TRIGGERS -- -- -------- -- DELIMITER | CREATE TRIGGER before_insert_adoption BEFORE INSERT ON Adoption FOR EACH ROW BEGIN IF NEW.paye != TRUE AND NEW.paye != FALSE THEN INSERT INTO Erreur (erreur) VALUES ('Erreur : paye doit valoir TRUE (1) ou FALSE (0).'); ELSEIF NEW.date_adoption < NEW.date_reservation THEN INSERT INTO Erreur (erreur) VALUES ('Erreur : date_adoption doit être >= à date_reservation.'); END IF; END | CREATE TRIGGER after_insert_adoption AFTER INSERT ON Adoption FOR EACH ROW BEGIN UPDATE Animal SET disponible = FALSE WHERE id = NEW.animal_id; END | CREATE TRIGGER before_update_adoption BEFORE UPDATE ON Adoption FOR EACH ROW BEGIN IF NEW.paye != TRUE AND NEW.paye != FALSE THEN INSERT INTO Erreur (erreur) VALUES ('Erreur : paye doit valoir TRUE (1) ou FALSE (0).'); ELSEIF NEW.date_adoption < NEW.date_reservation THEN INSERT INTO Erreur (erreur) VALUES ('Erreur : date_adoption doit être >= à date_reservation.'); END IF; END | CREATE TRIGGER after_update_adoption AFTER UPDATE ON Adoption FOR EACH ROW BEGIN IF OLD.animal_id <> NEW.animal_id THEN UPDATE Animal SET disponible = TRUE WHERE id = OLD.animal_id; UPDATE Animal SET disponible = FALSE WHERE id = NEW.animal_id; END IF; END | CREATE TRIGGER after_delete_adoption AFTER DELETE ON Adoption FOR EACH ROW BEGIN UPDATE Animal SET disponible = TRUE WHERE id = OLD.animal_id; END | CREATE TRIGGER before_insert_animal BEFORE INSERT ON Animal FOR EACH ROW BEGIN IF NEW.sexe IS NOT NULL AND NEW.sexe != 'M' AND NEW.sexe != 'F' THEN INSERT INTO Erreur (erreur) VALUES ('Erreur : sexe doit valoir "M", "F" ou NULL.'); END IF; END | CREATE TRIGGER before_update_animal BEFORE UPDATE ON Animal FOR EACH ROW BEGIN IF NEW.sexe IS NOT NULL AND NEW.sexe != 'M' AND NEW.sexe != 'F' THEN SET NEW.sexe = NULL; END IF; END | CREATE TRIGGER after_update_animal AFTER UPDATE ON Animal FOR EACH ROW BEGIN INSERT INTO Animal_histo ( id, sexe, date_naissance, nom, commentaires, espece_id, race_id, mere_id, pere_id, disponible, date_histo, utilisateur_histo, evenement_histo) VALUES ( OLD.id, OLD.sexe, OLD.date_naissance, OLD.nom, OLD.commentaires, OLD.espece_id, OLD.race_id, OLD.mere_id, OLD.pere_id, OLD.disponible, NOW(), CURRENT_USER(), 'UPDATE'); END | CREATE TRIGGER after_delete_animal AFTER DELETE ON Animal FOR EACH ROW BEGIN INSERT INTO Animal_histo ( id, sexe, date_naissance, nom, commentaires, espece_id, race_id, mere_id, pere_id, disponible, date_histo, utilisateur_histo, evenement_histo) VALUES ( OLD.id, OLD.sexe, OLD.date_naissance, OLD.nom, OLD.commentaires, OLD.espece_id, OLD.race_id, OLD.mere_id, OLD.pere_id, OLD.disponible, NOW(), CURRENT_USER(), 'DELETE'); END | CREATE TRIGGER before_delete_espece BEFORE DELETE ON Espece FOR EACH ROW BEGIN DELETE FROM Race WHERE espece_id = OLD.id; END | CREATE TRIGGER before_insert_race BEFORE INSERT ON Race FOR EACH ROW BEGIN SET NEW.date_insertion = NOW(); SET NEW.utilisateur_insertion = CURRENT_USER(); SET NEW.date_modification = NOW(); SET NEW.utilisateur_modification = CURRENT_USER(); END | CREATE TRIGGER before_update_race BEFORE UPDATE ON Race FOR EACH ROW BEGIN SET NEW.date_modification = NOW(); SET NEW.utilisateur_modification = CURRENT_USER(); END | CREATE TRIGGER before_delete_race BEFORE DELETE ON Race FOR EACH ROW BEGIN UPDATE Animal SET race_id = NULL WHERE race_id = OLD.id; END | DELIMITER ; |
- Création d'une vue
- Sélection des données d'une vue
- Modification et suppression d'une vue
- Utilité des vues
- Algorithmes
- Modification des données d'une vue
Création d'une vue
Le principe
Pour notre élevage, la requête suivante est très utile.
1 2 3 4 5 6 | SELECT Animal.id, Animal.sexe, Animal.date_naissance, Animal.nom, Animal.commentaires, Animal.espece_id, Animal.race_id, Animal.mere_id, Animal.pere_id, Animal.disponible, Espece.nom_courant AS espece_nom, Race.nom AS race_nom FROM Animal INNER JOIN Espece ON Animal.espece_id = Espece.id LEFT JOIN Race ON Animal.race_id = Race.id; |
Avec ou sans clause WHERE
, il arrive régulièrement qu'on veuille trouver des renseignements sur nos animaux, y compris leur race et leur espèce (et, seul, l'id contenu dans Animal n'est pas une information très explicite). Il serait donc bien pratique de pouvoir stocker cette requête plutôt que de devoir la retaper en entier à chaque fois.
C'est très exactement le principe d'une vue : on stocke une requête SELECT
en lui donnant un nom, et on peut ensuite appeler directement la vue par son nom.
Quelques remarques importantes :
- Il s'agit bien d'objets de la base de données, stockés de manière durable, comme le sont les tables ou les procédures stockées.
- C'est donc bien différent des requêtes préparées, qui ne sont définies que le temps d'une session, et qui ont un tout autre but.
- Ce qui est stocké est la requête, et non pas les résultats de celle-ci. On ne gagne absolument rien en terme de performance en utilisant une vue plutôt qu'en faisant une requête directement sur les tables.
Création
Pour créer une vue, on utilise tout simplement la commande CREATE VIEW
, dont voici la syntaxe :
1 2 | CREATE [OR REPLACE] VIEW nom_vue AS requete_select; |
La clause OR REPLACE
est facultative. Si elle est fournie, la vue nom_vue sera soit créée si elle n'existe pas, soit remplacée si elle existait déjà. Si OR REPLACE
est omise et qu'une vue portant le même nom a été précédemment définie, cela déclenchera une erreur.
Donc, si l'on reprend la requête précédente, voici comment créer une vue pour stocker celle-ci :
1 2 3 4 5 6 7 | CREATE VIEW V_Animal_details AS SELECT Animal.id, Animal.sexe, Animal.date_naissance, Animal.nom, Animal.commentaires, Animal.espece_id, Animal.race_id, Animal.mere_id, Animal.pere_id, Animal.disponible, Espece.nom_courant AS espece_nom, Race.nom AS race_nom FROM Animal INNER JOIN Espece ON Animal.espece_id = Espece.id LEFT JOIN Race ON Animal.race_id = Race.id; |
Dorénavant, plus besoin de retaper cette requête, il suffit de travailler à partir de la vue, comme s'il s'agissait d'une table :
1 | SELECT * FROM V_Animal_details; |
J'ai préfixé le nom de la vue par "V_". Il s'agit d'une convention que je vous conseille fortement de respecter. Cela permet de savoir au premier coup d’œil si l'on travaille avec une vraie table, ou avec une vue.
D'ailleurs, si l'on demande la liste des tables de la base de données, on peut voir que la vue est reprise (bien qu'il ne s'agit pas d'une table, mais d'une requête SELECT
stockée, j'insiste).
1 | SHOW TABLES; |
Tables_in_elevage |
---|
Adoption |
Animal |
Animal_histo |
Client |
Erreur |
Espece |
Race |
V_Animal_details |
Les colonnes de la vue
Comme pour les tables, on peut utiliser la commande DESCRIBE
pour voir les différentes colonnes de notre vue.
1 | DESCRIBE V_Animal_details; |
Field |
Type |
Null |
Key |
Default |
Extra |
---|---|---|---|---|---|
id |
smallint(6) unsigned |
NO |
0 |
||
sexe |
char(1) |
YES |
NULL |
||
date_naissance |
datetime |
NO |
NULL |
||
nom |
varchar(30) |
YES |
NULL |
||
commentaires |
text |
YES |
NULL |
||
espece_id |
smallint(6) unsigned |
NO |
NULL |
||
race_id |
smallint(6) unsigned |
YES |
NULL |
||
mere_id |
smallint(6) unsigned |
YES |
NULL |
||
pere_id |
smallint(6) unsigned |
YES |
NULL |
||
disponible |
tinyint(1) |
YES |
1 |
||
espece_nom |
varchar(40) |
NO |
NULL |
||
race_nom |
varchar(40) |
YES |
NULL |
Comme on pouvait s'y attendre, les noms de colonnes ont été déterminés par la clause SELECT
de la requête définissant la vue. S'il n'y avait pas d'alias pour la colonne, c'est simplement le même nom que dans la table d'origine (date_naissance par exemple), et si un alias a été donné, il est utilisé (espece_nom par exemple).
Lister les colonnes dans CREATE VIEW
Il existe une autre possibilité que les alias dans la clause SELECT
pour nommer les colonnes d'une vue. Il suffit de lister les colonnes juste après le nom de la vue, lors de la création de celle-ci. La commande suivante est l'équivalent de la précédente pour la création de V_Animal_details, mais cette fois sans alias dans la requête SELECT
.
1 2 3 4 5 6 7 | CREATE VIEW V_Animal_details (id, sexe, date_naissance, nom, commentaires, espece_id, race_id, mere_id, pere_id, disponible, espece_nom, race_nom) AS SELECT Animal.id, Animal.sexe, Animal.date_naissance, Animal.nom, Animal.commentaires, Animal.espece_id, Animal.race_id, Animal.mere_id, Animal.pere_id, Animal.disponible, Espece.nom_courant, Race.nom FROM Animal INNER JOIN Espece ON Animal.espece_id = Espece.id LEFT JOIN Race ON Animal.race_id = Race.id; |
Il faut toujours vérifier que les colonnes sélectionnées sont dans le bon ordre par rapport à la liste des noms, car la correspondance se fait uniquement sur la base de la position. MySQL ne lèvera pas le petit doigt si l'on nomme "espece" une colonne contenant le sexe.
Doublons interdits
Comme pour une table, il est impossible que deux colonnes ayant le même nom cohabitent dans une même vue.
1 2 3 4 | CREATE VIEW V_test AS SELECT Animal.id, Espece.id FROM Animal INNER JOIN Espece ON Animal.espece_id = Espece.id; |
1 | ERROR 1060 (42S21): Duplicate column name 'id'
|
Pour pouvoir créer cette vue, il est nécessaire de renommer une des deux colonnes dans la vue (soit avec un alias, soit en listant les colonnes juste après CREATE VIEW nom_vue
).
Requête SELECT stockée dans la vue
Que peut-on mettre dans la requête SELECT
qui sert à définir la vue ?
La requête définissant une vue peut être n'importe quelle requête SELECT
, à quelques exceptions près.
- Il n'est pas possible d'utiliser une requête
SELECT
dont la clauseFROM
contient une sous-requêteSELECT
. - La requête ne peut pas faire référence à des variables utilisateur, des variables système, ni même des variables locales (dans le cas d'une vue définie par une procédure stockée).
- Toutes les tables (ou vues) mentionnées dans la requête doivent exister (au moment de la création du moins).
En dehors de ça, carte blanche ! La requête peut contenir une clause WHERE
, une clause GROUP BY
, des fonctions (scalaires ou d’agrégation), des opérations mathématiques, une autre vue, des jointures, etc.
Exemple 1 : une vue pour les chiens.
L'employé de l'élevage préposé aux chiens peut créer une vue sur ceux-ci, afin de ne pas devoir ajouter une clause WHERE espece_id = 1
à chacune de ses requêtes.
1 2 3 4 | CREATE VIEW V_Chien AS SELECT id, sexe, date_naissance, nom, commentaires, espece_id, race_id, mere_id, pere_id, disponible FROM Animal WHERE espece_id = 1; |
Exemple 2 : combien de chats possédons-nous ?
Il est tout à fait possible de créer une vue définie par une requête qui compte le nombre d'animaux de chaque espèce que l'on possède.
1 2 3 4 5 | CREATE OR REPLACE VIEW V_Nombre_espece AS SELECT Espece.id, COUNT(Animal.id) AS nb FROM Espece LEFT JOIN Animal ON Animal.espece_id = Espece.id GROUP BY Espece.id; |
Exemple 3 : vue sur une vue.
La vue V_Chien sélectionne les chiens. Créons la vue V_Chien_race qui sélectionne les chiens dont on connaît la race. On peut bien sûr créer cette vue à partir de la table Animal, mais on peut aussi repartir simplement de la vue V_Chien.
1 2 3 4 | CREATE OR REPLACE VIEW V_Chien_race AS SELECT id, sexe, date_naissance, nom, commentaires, espece_id, race_id, mere_id, pere_id, disponible FROM V_Chien WHERE race_id IS NOT NULL; |
Exemple 4 : expression dans une vue.
Voici un exemple de vue définie par une requête contenant une expression, qui sélectionne les espèces, avec leur prix en dollars.
1 2 3 | CREATE VIEW V_Espece_dollars AS SELECT id, nom_courant, nom_latin, description, ROUND(prix*1.31564, 2) AS prix_dollars FROM Espece; |
La requête SELECT est "figée"
La requête SELECT
définissant une vue est figée : les changements de structure faits par la suite sur la ou les tables sous-jacentes n'influent pas sur la vue.
Par exemple, si l'on crée une vue V_Client toute simple.
1 2 3 | CREATE VIEW V_Client AS SELECT * FROM Client; |
Cette vue sélectionne les huit colonnes de Client (id, nom, prenom, adresse, code_postal, ville, pays, email). Une fois cette vue créée, elle est figée, et l'ajout d'une colonne dans la table Client ne changera pas les résultats d'une requête sur V_Client. Cette vue ne sélectionnera jamais que id, nom, prenom, adresse, code_postal, ville, pays et email, malgré le caractère * dans la clause SELECT
. Pour que V_Client sélectionne la nouvelle colonne de Client, il faudrait recréer la vue pour l'inclure.
Exemple : ajout d'une colonne date_naissance à la table Client.
1 2 3 | ALTER TABLE Client ADD COLUMN date_naissance DATE; DESCRIBE V_Client; |
Field |
Type |
Null |
Key |
Default |
Extra |
---|---|---|---|---|---|
id |
smallint(5) unsigned |
NO |
0 |
||
nom |
varchar(100) |
NO |
NULL |
||
prenom |
varchar(60) |
NO |
NULL |
||
adresse |
varchar(200) |
YES |
NULL |
||
code_postal |
varchar(6) |
YES |
NULL |
||
ville |
varchar(60) |
YES |
NULL |
||
pays |
varchar(60) |
YES |
NULL |
||
|
varbinary(100) |
YES |
NULL |
Tri des données directement dans la vue
Si l'on met un ORDER BY
dans la requête définissant une vue, celui-ci prendra effet, sauf si l'on fait une requête sur la vue avec un ORDER BY
. Dans ce cas, c'est ce dernier qui prime, et l'ORDER BY
originel est ignoré.
Exemple
1 2 3 4 5 6 7 8 9 10 11 12 | CREATE OR REPLACE VIEW V_Race AS SELECT Race.id, nom, Espece.nom_courant AS espece FROM Race INNER JOIN Espece ON Espece.id = Race.espece_id ORDER BY nom; SELECT * FROM V_Race; -- Sélection sans ORDER BY, on prend l'ORDER BY de la définition SELECT * FROM V_Race ORDER BY espece; -- Sélection avec ORDER BY, c'est celui-là qui sera pris en compte |
La première requête donnera bien les races par ordre alphabétique, les races de chiens et les races de chats sont mélangées. Par contre, dans la deuxième, on a d'abord toutes les races de chats, puis toutes les races de chiens.
Comportement d'autres clauses de SELECT
En ce qui concerne notamment les clauses LIMIT
et DISTINCT
(et son opposé, ALL
), le comportement est indéfini. Dans le cas où l'on fait une requête avec un LIMIT
sur une vue dont la requête possède aussi un LIMIT
, on ne sait pas quelle clause, de la définition ou de la sélection, sera appliquée. Par conséquent, il est déconseillé d'inclure ces clauses dans la définition d'une vue.
Sélection des données d'une vue
Une fois la vue créée, on peut bien entendu faire plus qu'un simple SELECT * FROM la_vue;
: on peut tout simplement traiter cette vue comme une table, et donc ajouter des clauses WHERE
, GROUP BY
, des fonctions, des jointures et tout ce que l'on veut !
Exemple 1 : on sélectionne les rats bruns à partir de la vue V_Animal_details.
1 2 3 | SELECT id, nom, espece_nom, date_naissance, commentaires, disponible FROM V_Animal_details WHERE espece_nom = 'Rat brun'; |
id |
nom |
espece_nom |
date_naissance |
commentaires |
disponible |
---|---|---|---|---|---|
69 |
Baba |
Rat brun |
2012-02-13 15:45:00 |
NULL |
0 |
70 |
Bibo |
Rat brun |
2012-02-13 15:48:00 |
Agressif |
1 |
72 |
Momy |
Rat brun |
2008-02-01 02:25:00 |
NULL |
1 |
73 |
Popi |
Rat brun |
2007-03-11 12:45:00 |
NULL |
1 |
75 |
Mimi |
Rat brun |
2007-03-12 22:03:00 |
NULL |
0 |
Exemple 2 : on sélectionne le nombre d'animaux par espèce avec la vue V_Nombre_espece, en ajoutant une jointure sur la table Espece pour avoir le nom des espèces en plus de leur id.
1 2 3 | SELECT V_Nombre_espece.id, Espece.nom_courant, V_Nombre_espece.nb FROM V_Nombre_espece INNER JOIN Espece ON Espece.id = V_Nombre_espece.id; |
id |
nom_courant |
nb |
---|---|---|
1 |
Chien |
21 |
2 |
Chat |
20 |
3 |
Tortue d'Hermann |
14 |
4 |
Perroquet amazone |
4 |
5 |
Rat brun |
5 |
Exemple 3 : on sélectionne le nombre de chiens qu'on possède pour chaque race, en utilisant un regroupement sur la vue V_Chien_race, avec jointure sur Race pour avoir le nom de la race.
1 2 3 4 | SELECT Race.nom, COUNT(V_Chien_race.id) FROM Race INNER JOIN V_Chien_race ON Race.id = V_Chien_race.race_id GROUP BY Race.nom; |
nom |
COUNT(V_Chien_race.id) |
---|---|
Berger allemand |
8 |
Berger blanc suisse |
4 |
Rottweiller |
1 |
Modification et suppression d'une vue
Modification
CREATE OR REPLACE
Comme déjà mentionné, pour modifier une vue, on peut tout simplement ajouter la clause OR REPLACE
à la requête de création de la vue, et lui donner le nom de la vue à modifier. L'ancienne vue sera alors remplacée.
Si le nom de votre vue est incorrect et ne correspond à aucune vue existante, aucune erreur ne sera renvoyée, et vous créerez tout simplement une nouvelle vue.
ALTER
Il existe aussi la commande ALTER VIEW
, qui aura le même effet que CREATE OR REPLACE VIEW
si la vue existe bel et bien. Dans le cas contraire, ALTER VIEW
générera une erreur.
Syntaxe :
1 2 | ALTER VIEW nom_vue [(liste_colonnes)] AS requete_select |
Exemples : les deux requêtes suivantes ont exactement le même effet : on modifie la vue V_espece_dollars pour mettre à jour le taux de change du dollar.
1 2 3 4 5 6 7 | CREATE OR REPLACE VIEW V_Espece_dollars AS SELECT id, nom_courant, nom_latin, description, ROUND(prix*1.30813, 2) AS prix_dollars FROM Espece; ALTER VIEW V_Espece_dollars AS SELECT id, nom_courant, nom_latin, description, ROUND(prix*1.30813, 2) AS prix_dollars FROM Espece; |
Suppression
Pour supprimer une vue, on utilise simplement DROP VIEW [IF EXISTS] nom_vue;
.
Exemple : suppression de V_Race.
1 | DROP VIEW V_Race; |
Utilité des vues
Au-delà de la légendaire paresse des informaticiens, bien contents de ne pas devoir retaper la même requête encore et encore, les vues sont utilisées pour différentes raisons, dont voici les principales.
Clarification et facilitation des requêtes
Lorsqu'une requête implique un grand nombre de tables, ou nécessite des fonctions, des regroupements, etc., même une bonne indentation ne suffit pas toujours à rendre la requête claire. Qui plus est, plus la requête est longue et complexe, plus l'ajout de la moindre condition peut se révéler pénible. Avoir une vue pour des requêtes complexes permet de simplifier et de clarifier les requêtes.
Exemple : on veut savoir quelles espèces rapportent le plus, année après année. Comme c'est une question importante pour le développement de l'élevage, et qu'elle reviendra souvent, on crée une vue, que l'on pourra interroger facilement.
1 2 3 4 5 6 | CREATE OR REPLACE VIEW V_Revenus_annee_espece AS SELECT YEAR(date_reservation) AS annee, Espece.id AS espece_id, SUM(Adoption.prix) AS somme, COUNT(Adoption.animal_id) AS nb FROM Adoption INNER JOIN Animal ON Animal.id = Adoption.animal_id INNER JOIN Espece ON Animal.espece_id = Espece.id GROUP BY annee, Espece.id; |
Avec des requêtes toutes simples, on peut maintenant obtenir des informations résultant de requêtes complexes, par exemple :
1. Les revenus obtenus par année
1 2 3 | SELECT annee, SUM(somme) AS total FROM V_Revenus_annee_espece GROUP BY annee; |
annee |
total |
---|---|
2007 |
2315.00 |
2008 |
3565.00 |
2009 |
400.00 |
2010 |
340.00 |
2011 |
1755.00 |
2012 |
3045.00 |
2. Les revenus obtenus pour chaque espèce, toutes années confondues
1 2 3 4 | SELECT Espece.nom_courant AS espece, SUM(somme) AS total FROM V_Revenus_annee_espece INNER JOIN Espece ON V_Revenus_annee_espece.espece_id = Espece.id GROUP BY espece; |
espece |
total |
---|---|
Chat |
6480.00 |
Chien |
2400.00 |
Perroquet amazone |
2100.00 |
Rat brun |
20.00 |
Tortue d'Hermann |
420.00 |
3. Les revenus moyens générés par la vente d'un individu de l'espèce
1 2 3 4 | SELECT Espece.nom_courant AS espece, SUM(somme)/SUM(nb) AS moyenne FROM V_Revenus_annee_espece INNER JOIN Espece ON V_Revenus_annee_espece.espece_id = Espece.id GROUP BY espece; |
espece |
moyenne |
---|---|
Chat |
720.000000 |
Chien |
342.857143 |
Perroquet amazone |
700.000000 |
Rat brun |
10.000000 |
Tortue d'Hermann |
140.000000 |
Création d'une interface entre l'application et la base de données
Lorsque l'on a une base de données exploitée par une application (écrite dans un langage de programmation comme Java, ou PHP par exemple), c'est souvent dans cette application que sont construites les requêtes qui vont insérer, modifier, et sélectionner les données de la base. Si pour une raison ou une autre (mauvaise conception de la base de données au départ, modèle de données qui s’étend fortement,…) la structure des tables de la base change, il faut réécrire également l'application pour prendre en compte les modifications nécessaires pour les requêtes.
Cependant, si l'on a utilisé des vues, on peut éviter de réécrire toutes ces requêtes, ou du moins limiter le nombre de requêtes à réécrire. Si les requêtes sont faites sur des vues, il suffit en effet de modifier la définition de ces vues pour qu'elles fonctionnent avec la nouvelle structure.
Exemple
On a créé une vue V_Client, qui permet de voir le contenu de notre table Client (sauf la date de naissance, ajoutée après la définition de la vue).
Si un beau jour on décidait de stocker les adresses postales dans une table à part, il faudrait modifier la structure de notre base. Au lieu d'une seule table Client, on en aurait deux : Client(id, nom, prenom, date_naissance, email, adresse_id) et Adresse(id, rue_numero, code_postal, ville, pays).
Pour que l'application puisse continuer à sélectionner les personnes et leur adresse sans qu'on doive la modifier, il suffirait de changer la requête définissant la vue :
1 2 3 4 | CREATE OR REPLACE VIEW V_Client -- le OR REPLACE indispensable (ou on utilise ALTER VIEW) AS SELECT Client.id, nom, prenom, rue_numero AS adresse, code_postal, ville, pays, email, date_naissance FROM Client LEFT JOIN Adresse ON Client.adresse_id = Adresse.id -- LEFT JOIN au cas où certains clients n'auraient pas d'adresse définie |
Le changement de structure de la base de données serait ainsi transparent pour l'application (du moins en ce qui concerne la sélection des clients) !
Restriction des données visibles par les utilisateurs
La gestion des utilisateurs et de leurs droits fera l'objet d'un prochain chapitre. Sachez simplement que pour chaque utilisateur, il est possible de définir des droits particuliers pour chaque table et chaque vue (entre autres choses). On peut par exemple autoriser l'utilisateur A à faire des requêtes d'insertion, modification, suppression et sélection (le fameux CRUD) sur la table T1, mais n'autoriser l'utilisateur B qu'à faire des sélections sur la table T1. Et imaginons qu'il existe aussi un utilisateur C, auquel on veut donner l'autorisation de faire des requêtes de sélection sur la table T1, mais auquel il faudrait cacher certaines colonnes qui contiennent des données sensibles, ou certaines lignes auxquelles il ne devrait pas accéder. Il suffit de créer la vue V_T1, n'ayant accès qu'aux colonnes/lignes "publiques" de la table T1, et de donner à C les droits sur la vue V_T1, mais pas sur la table T1.
Exemple
Le stagiaire travaillant dans notre élevage s'occupe exclusivement des chats, et ne doit pas avoir accès aux commentaires. On ne lui donne donc pas accès à la table Animal, mais à une vue V_Animal_stagiaire créée de la manière suivante :
1 2 3 4 | CREATE VIEW V_Animal_stagiaire AS SELECT id, nom, sexe, date_naissance, espece_id, race_id, mere_id, pere_id, disponible FROM Animal WHERE espece_id = 2; |
Algorithmes
Lors de la création d'une vue, on peut définir quel algorithme sera utilisé par MySQL lors d'une sélection sur celle-ci.
1 2 3 4 | CREATE [OR REPLACE] [ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}] VIEW nom_vue AS requete_select |
Il s'agit d'une clause non standard, donc valable uniquement pour MySQL. Deux algorithmes différents peuvent être utilisés : MERGE
et TEMPTABLE
.
Les algorithmes interviennent au moment où l'on sélectionne des données de la vue, et pas directement à la création de celle-ci.
MERGE
Si l'algorithme MERGE
(fusion en anglais) a été choisi, lorsque l'on sélectionne des données de la vue, MySQL va fusionner la requête SELECT
qui définit la vue avec les clauses de sélections. Faire une sélection sur une vue qui utilise cet algorithme revient donc à faire une requête directement sur les tables sous-jacentes.
Comme ce n'est pas nécessairement très clair, voici deux exemples.
Exemple 1
On a créé la vue V_Animal_details à partir de la requête suivante :
1 2 3 4 5 6 | SELECT Animal.id, Animal.sexe, Animal.date_naissance, Animal.nom, Animal.commentaires, Animal.espece_id, Animal.race_id, Animal.mere_id, Animal.pere_id, Animal.disponible, Espece.nom_courant AS espece_nom, Race.nom AS race_nom FROM Animal INNER JOIN Espece ON Animal.espece_id = Espece.id LEFT JOIN Race ON Animal.race_id = Race.id; |
Ensuite, on fait une sélection sur cette vue :
1 2 3 | SELECT * FROM V_Animal_details WHERE MONTH(date_naissance) = 6; |
Si c'est l'algorithme MERGE
qui est utilisé, MySQL va fusionner :
- la requête définissant la vue :
1 2 3 4 5 6 | SELECT Animal.id, Animal.sexe, Animal.date_naissance, Animal.nom, Animal.commentaires, Animal.espece_id, Animal.race_id, Animal.mere_id, Animal.pere_id, Animal.disponible, Espece.nom_courant AS espece_nom, Race.nom AS race_nom FROM Animal INNER JOIN Espece ON Animal.espece_id = Espece.id LEFT JOIN Race ON Animal.race_id = Race.id; |
- les clauses de la requête de sélection :
1 | WHERE MONTH(date_naissance) = 6; |
Au final, faire cette requête sur la vue provoquera l'exécution de la requête suivante :
1 2 3 4 5 6 7 | SELECT Animal.id, Animal.sexe, Animal.date_naissance, Animal.nom, Animal.commentaires, Animal.espece_id, Animal.race_id, Animal.mere_id, Animal.pere_id, Animal.disponible, Espece.nom_courant AS espece_nom, Race.nom AS race_nom FROM Animal INNER JOIN Espece ON Animal.espece_id = Espece.id LEFT JOIN Race ON Animal.race_id = Race.id WHERE MONTH(date_naissance) = 6; |
Exemple 2
Si l'on exécute la requête suivante sur la vue V_Chien, et que l'algorithme MERGE
est utilisé :
1 2 3 | SELECT nom, date_naissance FROM V_Chien WHERE pere_id IS NOT NULL; |
MySQL va fusionner les deux requêtes, et exécuter ceci :
1 2 3 4 | SELECT nom, date_naissance FROM Animal WHERE espece_id = 1 AND pere_id IS NOT NULL; |
TEMPTABLE
L'algorithme TEMPTABLE
, par contre, crée une table temporaire contenant les résultats de la requête définissant la vue puis, par la suite, exécute la requête de sélection sur cette table temporaire.
Donc, si l'on exécute la requête suivante sur la vue V_Chien :
1 2 3 | SELECT nom, date_naissance FROM V_Chien WHERE pere_id IS NOT NULL; |
Avec l'algorithme TEMPTABLE
, la requête définissant la vue va être exécutée et ses résultats stockés dans une table temporaire.
1 2 3 | SELECT id, sexe, date_naissance, nom, commentaires, espece_id, race_id, mere_id, pere_id, disponible FROM Animal WHERE espece_id = 1; |
Ensuite, sur cette table temporaire, va être exécutée la requête finale :
1 2 3 | SELECT nom, date_naissance FROM table_temporaire WHERE pere_id IS NOT NULL; |
Algorithme par défaut et conditions
Il existe une troisième option possible pour la clause ALGORITHM
dans la requête de création des vues : UNDEFINED
.
Par défaut, si on ne précise pas d'algorithme pour la vue, c'est l'option UNDEFINED
qui est utilisée. Cette option laisse MySQL décider lui-même de l'algorithme qu'il appliquera.
Si c'est possible, MERGE
sera utilisé, car cet algorithme est plus performant que TEMPTABLE
. Cependant, toutes les vues ne peuvent pas utiliser l'algorithme MERGE
. En effet, une vue utilisant un ou plusieurs des éléments suivants ne pourra pas utiliser MERGE
:
DISTINCT
;LIMIT
;- une fonction d'agrégation (
SUM()
,COUNT()
,MAX()
, etc.) ; GROUP BY
;HAVING
;UNION
;- une sous-requête dans la clause
SELECT
.
Modification des données d'une vue
On a tendance à penser que les vues ne servent que pour la sélection de données. En réalité, il est possible de modifier, insérer et supprimer des données par l'intermédiaire d'une vue. Les requêtes sont les mêmes que pour insérer, modifier et supprimer des données à partir d'une table (si ce n'est qu'on met le nom de la vue au lieu du nom de la table bien sûr).
Cependant, pour qu'une vue ne soit pas en "lecture seule", elle doit répondre à une série de conditions.
Conditions pour qu'une vue permette de modifier des données (requêtes UPDATE)
Jointures
Il est possible de modifier des données à partir d'une vue définie avec une jointure, à condition de ne modifier qu'une seule table.
Exemple
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | -- Modifie Animal UPDATE V_Animal_details SET commentaires = 'Rhume chronique' WHERE id = 21; -- Modifie Race UPDATE V_Animal_details SET race_nom = 'Maine Coon' WHERE race_nom = 'Maine coon'; -- Erreur UPDATE V_Animal_details SET commentaires = 'Vilain oiseau', espece_nom = 'Perroquet pas beau' -- commentaires vient de Animal, et espece_nom vient de Espece WHERE espece_id = 4; |
Les deux premières modifications ne posent aucun problème, mais la troisième échouera, car elle modifie des colonnes appartenant à deux tables différentes.
1 | ERROR 1393 (HY000): Can not modify more than one base table through a join view 'elevage.v_animal_details'
|
Algorithme
La vue doit utiliser l'algorithme MERGE
(que l'algorithme ait été spécifié à la création, ou choisi par MySQL si l'algorithme n'a pas été défini). Par conséquent, les mêmes conditions que pour utiliser l'algorithme MERGE
s'appliquent. Les éléments suivants sont ainsi interdits dans une vue si l'on veut pouvoir modifier les données à partir de celle-ci :
DISTINCT
;LIMIT
;- une fonction d’agrégation (
SUM()
,COUNT()
,MAX()
, etc.) ; GROUP BY
;HAVING
;UNION
;- une sous-requête dans la clause
SELECT
.
Exemple : la vue V_Nombre_espece, qui utilise une fonction d'agrégation, ne permet pas de modifier les données.
1 2 3 | UPDATE V_Nombre_espece SET nb = 6 WHERE id = 4; |
1 | 1288 (HY000): The target table V_Nombre_espece of the UPDATE is not updatable
|
Autres conditions
- On ne peut pas modifier les données à partir d'une vue qui est elle-même définie à partir d'une vue qui ne permet pas la modification des données.
- Ce n'est pas non plus possible à partir d'une vue dont la clause
WHERE
contient une sous-requête faisant référence à une des tables de la clauseFROM
.
Conditions pour qu'une vue permette d'insérer des données (requêtes INSERT)
On peut insérer des données dans une vue si celle-ci respecte les mêmes conditions que pour la modification de données, ainsi que les conditions supplémentaires suivantes.
Valeurs par défaut
Toutes les colonnes n'ayant pas de valeur par défaut (et ne pouvant pas être NULL
) de la table dans laquelle on veut faire l'insertion doivent être référencées par la vue.
Exemple
1 2 | INSERT INTO V_Animal_stagiaire (nom, sexe, date_naissance, espece_id, race_id) VALUES ('Rocco', 'M', '2012-03-12', 1, 9); |
Ceci fonctionne. La colonne commentaires n'est pas référencée par V_Animal_stagiaire mais peut être NULL
(qui est donc sa valeur par défaut).
1 2 3 4 5 6 | CREATE VIEW V_Animal_mini AS SELECT id, nom, sexe, espece_id FROM Animal; INSERT INTO V_Animal_mini(nom, sexe, espece_id) VALUES ('Toxi', 'F', 1); |
Par contre, l'insertion dans V_Animal_mini échoue puisque date_naissance n'est pas référencée, ne peut pas être NULL
, et n'a pas de valeur par défaut.
1 | ERROR 1423 (HY000): Field of view 'elevage.v_animal_mini' underlying table doesn't have a default value
|
Jointures
Les vues avec jointure peuvent supporter l'insertion si :
- il n'y a que des jointures internes ;
- l'insertion se fait sur une seule table (comme pour la modification).
Exemple
1 2 | INSERT INTO V_Animal_details (espece_nom, espece_nom_latin) VALUES ('Perruche terrestre', 'Pezoporus wallicus'); |
Il y a une jointure externe dans V_Animal_details, donc l'insertion ne fonctionnera pas.
1 | ERROR 1471 (HY000): The target table V_Animal_details of the INSERT is not insertable-into
|
Par contre, si l'on crée une table V_Animal_espece, avec uniquement une jointure interne, il n'y a aucun problème.
1 2 3 4 5 6 7 8 9 | CREATE OR REPLACE VIEW V_Animal_espece AS SELECT Animal.id, Animal.sexe, Animal.date_naissance, Animal.nom, Animal.commentaires, Animal.espece_id, Animal.race_id, Animal.mere_id, Animal.pere_id, Animal.disponible, Espece.nom_courant AS espece_nom, Espece.nom_latin AS espece_nom_latin FROM Animal INNER JOIN Espece ON Espece.id = Animal.espece_id; INSERT INTO V_Animal_espece (espece_nom, espece_nom_latin) VALUES ('Perruche terrestre', 'Pezoporus wallicus'); |
Expressions
Les colonnes de la vue doivent être de simples références à des colonnes, et non pas des expressions.
Exemple
Dans la vue V_Espece_dollars, la colonne prix_dollars correspond à ROUND(prix*1.30813, 2). Il n'est donc pas possible d'insérer des données à partir de cette vue.
1 2 | INSERT INTO V_Espece_dollars (nom_courant, nom_latin, prix_dollars) VALUES ('Perruche terrestre', 'Pezoporus wallicus', 30); |
1 | ERROR 1471 (HY000): The target table V_Espece_dollars of the INSERT is not insertable-into
|
Colonnes dupliquées
La même colonne ne peut pas être référencée deux fois dans la vue.
Exemple
Si l'on crée une vue avec deux fois la même colonne référencée, il est possible de modifier des données à partir de celle-ci, mais pas d'en insérer.
1 2 3 4 5 6 7 8 9 10 11 12 | CREATE VIEW V_Espece_2noms AS SELECT id, nom_courant, nom_latin, description, prix, nom_courant AS nom2 -- nom_courant est référencé deux fois FROM Espece; -- Modification, pas de problème UPDATE V_Espece_2noms SET description= 'Joli oiseau aux plumes majoritairement vert brillant', prix = 20.00 WHERE nom_courant = 'Perruche terrestre'; -- Insertion, impossible INSERT INTO V_Espece_2noms (nom_courant, nom_latin, prix) VALUES ('Perruche turquoisine', 'Neophema pulchella', 40); |
1 | ERROR 1471 (HY000): The target table V_Espece_2noms of the INSERT is not insertable-into
|
Conditions pour qu'une vue permette de supprimer des données (requêtes DELETE)
Il est possible de supprimer des données à partir d'une vue si et seulement si :
- il est possible de modifier des données à partir de cette vue ;
- cette vue est "mono-table" (une seule table sous-jacente).
Option de la vue pour la modification des données
Lors de la création d'une vue, on peut ajouter une option : WITH [LOCAL | CASCADED] CHECK OPTION
.
1 2 3 4 5 | CREATE [OR REPLACE] [ALGORITHM = {UNDEFINED | MERGE | TEMPTABLE}] VIEW nom_vue [(liste_colonnes)] AS requete_select [WITH [CASCADED | LOCAL] CHECK OPTION] |
Lorsque cette option est spécifiée, les modifications et insertions ne sont acceptées que si les données répondent aux conditions de la vue (c'est-à-dire aux conditions données par la clause WHERE
de la requête définissant la vue).
Exemples
Si la vue V_Animal_stagiaire (qui, pour rappel, sélectionne les chats uniquement) est définie avec WITH CHECK OPTION
, on ne peut pas modifier l'espece_id à partir de cette vue.
1 2 3 4 5 | CREATE OR REPLACE VIEW V_Animal_stagiaire AS SELECT id, nom, sexe, date_naissance, espece_id, race_id, mere_id, pere_id, disponible FROM Animal WHERE espece_id = 2 WITH CHECK OPTION; |
En effet, cette vue est définie avec la condition WHERE espece_id = 2
. Les modifications faites sur les données de cette vue doivent respecter cette condition.
1 2 3 | UPDATE V_Animal_stagiaire SET espece_id = 1 WHERE nom = 'Cracotte'; |
1 | ERROR 1369 (HY000): CHECK OPTION failed 'elevage.v_animal_stagiaire'
|
De même, l'insertion d'un animal dont l'espece_id n'est pas 2 sera refusée aussi.
1 2 3 4 5 | INSERT INTO V_Animal_stagiaire (sexe, date_naissance, espece_id, nom) VALUES ('F', '2011-09-21 15:14:00', 2, 'Bambi'); -- c'est un chat, pas de problème INSERT INTO V_Animal_stagiaire (sexe, date_naissance, espece_id, nom) VALUES ('M', '2011-03-11 05:54:00', 6, 'Tiroli'); -- c'est une perruche, impossible |
La première insertion ne pose aucun problème, mais la seconde échoue.
LOCAL ou CASCADED
LOCAL
: seules les conditions de la vue même sont vérifiées.CASCADED
: les conditions des vues sous-jacentes éventuelles sont également vérifiées. C'est l'option par défaut.
La vue V_Chien_race, par exemple, est définie à partir de la vue V_Chien. Si on ajoute WITH LOCAL CHECK OPTION
à V_Chien_race, les modifications et insertions dans cette vue devront vérifier uniquement les conditions de la vue elle-même, c'est-à-dire race_id IS NOT NULL
. Si par contre, c'est WITH CASCADED CHECK OPTION
qui est choisi, alors les modifications et insertions devront toujours vérifier les conditions de V_Chien_race, mais également celles de V_Chien (espece_id = 1
).
Exemple 1 : LOCAL
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | CREATE OR REPLACE VIEW V_Chien_race AS SELECT id, sexe, date_naissance, nom, commentaires, espece_id, race_id, mere_id, pere_id, disponible FROM V_Chien WHERE race_id IS NOT NULL WITH LOCAL CHECK OPTION; -- Modification -- -- ------------ -- UPDATE V_Chien_race SET race_id = NULL -- Ne respecte pas la condition de V_Chien_race WHERE nom = 'Zambo'; -- => Impossible UPDATE V_Chien_race SET espece_id = 2, race_id = 4 -- Ne respecte pas la condition de V_Chien WHERE nom = 'Java'; -- => possible puisque LOCAL CHECK OPTION -- Insertion -- -- --------- -- INSERT INTO V_Chien_race (sexe, date_naissance, nom, commentaires, espece_id, race_id) -- Respecte toutes les conditions VALUES ('M', '2012-02-28 03:05:00', 'Pumba', 'Prématuré, à surveiller', 1, 9); -- => Pas de problème INSERT INTO V_Chien_race (sexe, date_naissance, nom, commentaires, espece_id, race_id) -- La race n'est pas NULL, mais c'est un chat VALUES ('M', '2011-05-24 23:51:00', 'Lion', NULL, 2, 5); -- => pas de problème puisque LOCAL INSERT INTO V_Chien_race (sexe, date_naissance, nom, commentaires, espece_id, race_id) -- La colonne race_id est NULL VALUES ('F', '2010-04-28 13:01:00', 'Mouchou', NULL, 1, NULL); -- => impossible |
La première modification et la dernière insertion échouent donc avec l'erreur suivante :
1 | ERROR 1369 (HY000): CHECK OPTION failed 'elevage.v_chien_race'
|
Exemple 2 : CASCADED
1 2 3 4 5 6 7 8 9 10 11 12 13 | CREATE OR REPLACE VIEW V_Chien_race AS SELECT id, sexe, date_naissance, nom, commentaires, espece_id, race_id, mere_id, pere_id, disponible FROM V_Chien WHERE race_id IS NOT NULL WITH CASCADED CHECK OPTION; UPDATE V_Chien_race SET race_id = NULL -- Ne respecte pas la condition de V_Chien_race WHERE nom = 'Zambo'; -- => impossible UPDATE V_Chien_race SET espece_id = 2, race_id = 4 -- Ne respecte pas la condition de V_Chien WHERE nom = 'Fila'; -- => impossible aussi puisque CASCADED |
Cette fois, les deux modifications échouent.
En résumé
- Une vue est une requête
SELECT
que l'on stocke et à laquelle on donne un nom. - La requête
SELECT
stockée dans une vue peut utiliser des jointures, des clausesWHERE
,GROUP BY
, des fonctions (scalaires ou d’agrégation), etc. L'utilisation deDISTINCT
etLIMIT
est cependant déconseillée. - On peut sélectionner les données à partir d'une vue de la même manière qu'on le fait à partir d'une table. On peut donc utiliser des jointures, des fonctions, des
GROUP BY
, desLIMIT
,… - Les vues permettent de simplifier les requêtes, de créer une interface entre l'application et la base de données, et/ou restreindre finement l'accès en lecture des données aux utilisateurs.
- Sous certaines conditions, il est possible d'insérer, modifier et supprimer des données à partir d'une vue.