Séance 6: Langage SQL
Transcription
Séance 6: Langage SQL
Séance 6: Langage SQL INFO 0009 - Bases de données ULg March 16, 2015 1. Création de tables Format général: CREATE TABLE IF NOT EXISTS <NOM_TABLE>( <nom_var1> <type_var1> <option_var1>, ... <nom_varN> <type_varN> <option_varN>, PRIMARY KEY(nom_varI), FOREIGN KEY(nom_varJ) REFERENCES <TABLE>(<nom_var>), ... FOREIGN KEY(nom_varK) REFERENCES <TABLE>(<nom_var>) )ENGINE=InnoDB; Type : INT,BIGINT,CHAR,VARCHAR,DATE,... Options : NOT NULL, UNIQUE, PRIMARY KEY, AUTO INCREMENT,... Exercice 1 Créer les tables suivantes: 1. Personne(n national, nom, prenom, sexe, rue, numero, CP, CL) 2. Commune(code postal,nom commune) 3. Localite(code postal,code localite,nom localite) 4. Mariage(code mariage, n national mari, n national femme, date mariage, date divorce, code postal) 5. Enfant(n national enfant, n national pere, n national mere, date naissance) Création de tables: Exercice 1 Personne(n national, nom, prenom, sexe, rue, numero, CP, CL) CREATE TABLE IF NOT EXISTS Personne( Création de tables: Exercice 1 Personne(n national, nom, prenom, sexe, rue, numero, CP, CL) CREATE TABLE IF NOT EXISTS Personne( n_national BIGINT PRIMARY KEY, Création de tables: Exercice 1 Personne(n national, nom, prenom, sexe, rue, numero, CP, CL) CREATE TABLE IF NOT EXISTS Personne( n_national BIGINT PRIMARY KEY, nom VARCHAR(50) NOT NULL, Création de tables: Exercice 1 Personne(n national, nom, prenom, sexe, rue, numero, CP, CL) CREATE TABLE IF NOT EXISTS Personne( n_national BIGINT PRIMARY KEY, nom VARCHAR(50) NOT NULL, prenom VARCHAR(50) NOT NULL, Création de tables: Exercice 1 Personne(n national, nom, prenom, sexe, rue, numero, CP, CL) CREATE TABLE IF NOT EXISTS Personne( n_national BIGINT PRIMARY KEY, nom VARCHAR(50) NOT NULL, prenom VARCHAR(50) NOT NULL, sexe CHAR(1) NOT NULL, Création de tables: Exercice 1 Personne(n national, nom, prenom, sexe, rue, numero, CP, CL) CREATE TABLE IF NOT EXISTS Personne( n_national BIGINT PRIMARY KEY, nom VARCHAR(50) NOT NULL, prenom VARCHAR(50) NOT NULL, sexe CHAR(1) NOT NULL, rue VARCHAR(50) NOT NULL, Création de tables: Exercice 1 Personne(n national, nom, prenom, sexe, rue, numero, CP, CL) CREATE TABLE IF NOT EXISTS Personne( n_national BIGINT PRIMARY KEY, nom VARCHAR(50) NOT NULL, prenom VARCHAR(50) NOT NULL, sexe CHAR(1) NOT NULL, rue VARCHAR(50) NOT NULL, numero VARCHAR(5) NOT NULL, Création de tables: Exercice 1 Personne(n national, nom, prenom, sexe, rue, numero, CP, CL) CREATE TABLE IF NOT EXISTS Personne( n_national BIGINT PRIMARY KEY, nom VARCHAR(50) NOT NULL, prenom VARCHAR(50) NOT NULL, sexe CHAR(1) NOT NULL, rue VARCHAR(50) NOT NULL, numero VARCHAR(5) NOT NULL, code_postal INT NOT NULL, Création de tables: Exercice 1 Personne(n national, nom, prenom, sexe, rue, numero, CP, CL) CREATE TABLE IF NOT EXISTS Personne( n_national BIGINT PRIMARY KEY, nom VARCHAR(50) NOT NULL, prenom VARCHAR(50) NOT NULL, sexe CHAR(1) NOT NULL, rue VARCHAR(50) NOT NULL, numero VARCHAR(5) NOT NULL, code_postal INT NOT NULL, code_localite INT NOT NULL Création de tables: Exercice 1 Personne(n national, nom, prenom, sexe, rue, numero, CP, CL) CREATE TABLE IF NOT EXISTS Personne( n_national BIGINT PRIMARY KEY, nom VARCHAR(50) NOT NULL, prenom VARCHAR(50) NOT NULL, sexe CHAR(1) NOT NULL, rue VARCHAR(50) NOT NULL, numero VARCHAR(5) NOT NULL, code_postal INT NOT NULL, code_localite INT NOT NULL, FOREIGN KEY (code_postal,code_localite) REFERENCES Localite(code_postal,code_localite) Création de tables: Exercice 1 Personne(n national, nom, prenom, sexe, rue, numero, CP, CL) CREATE TABLE IF NOT EXISTS Personne( n_national BIGINT PRIMARY KEY, nom VARCHAR(50) NOT NULL, prenom VARCHAR(50) NOT NULL, sexe CHAR(1) NOT NULL, rue VARCHAR(50) NOT NULL, numero VARCHAR(5) NOT NULL, code_postal INT NOT NULL, code_localite INT NOT NULL, FOREIGN KEY (code_postal,code_localite) REFERENCES Localite(code_postal,code_localite) )engine=InnoDB; Exercice 1 Créer les tables suivantes: 1. Commune(code postal,nom commune) 2. Localite(code postal,code localite,nom localite) 3. Mariage(code mariage, n national mari, n national femme, date mariage, date divorce, code postal) 4. Enfant(n national enfant, n national pere, n national mere, date naissance) Commune(code postal,nom commune) CREATE TABLE IF NOT EXISTS Commune( code_postal INT PRIMARY KEY, nom_commune VARCHAR(50) NOT NULL, )ENGINE=InnoDB; Localite(code postal,code localite,nom localite) CREATE TABLE IF NOT EXISTS Localite( code_postal INT NOT NULL, code_localite INT NOT NULL, nom_localite VARCHAR(50) NOT NULL, PRIMARY KEY (code_postal,code_localite) FOREIGN KEY code_postal REFERENCES Commune(code_postal) )ENGINE=InnoDB; ou... CREATE TABLE IF NOT EXISTS Localite( code_postal INT NOT NULL, code_localite INT NOT NULL, nom_localite VARCHAR(50) NOT NULL, PRIMARY KEY (code_postal,code_localite) )ENGINE=InnoDB; ALTER TABLE Localite ADD CONSTRAINT ‘cstr_localite_1‘ FOREIGN KEY code_postal REFERENCES Commune(code_postal) Mariage(code mariage, n national mari, n national femme, date mariage, date divorce, code postal) CREATE TABLE IF NOT EXISTS Mariage( code_mariage INT PRIMARY KEY, n_national_mari BIGINT NOT NULL, n_national_femme BIGINT NOT NULL, date_mariage DATE NOT NULL, date_divorce DATE, code_postal INT NOT NULL, FOREIGN KEY code_postal REFERENCES Commune(code_postal), FOREIGN KEY n_national_mari REFERENCES Personne(n_national), FOREIGN KEY n_national_femme REFERENCES Personne(n_national), CONSTRAINT CHECK_DATES CHECK (date_divorce >= date_mariage) )ENGINE=InnoDB; Enfant(n national enfant, n national pere, n national mere, date naissance) CREATE TABLE IF NOT EXISTS Enfant( n_national_enfant BIGINT NOT NULL, n_national_pere BIGINT, n_national_mere BIGINT NOT NULL, date_naissance DATE NOT NULL, PRIMARY KEY n_national_enfant, FOREIGN KEY n_national_enfant REFERENCES Personne(n_national), FOREIGN KEY n_national_mere REFERENCES Personne(n_national), FOREIGN KEY n_national_pere REFERENCES Personne(n_national) )ENGINE=InnoDB; Requêtes Requêtes SELECT * FROM <nom_table> Sélectionne tous les tuples de la table Requêtes Table ETUDIANT: Nom Prénom Dupont Georges Dupont Henri Durant Francis Durant Gustave Proviste Alain Age 17 16 17 17 14 Points 12 13 15 14 12 Age 17 16 17 17 14 Points 12 13 15 14 12 SELECT * FROM ETUDIANTS Nom Dupont Dupont Durant Durant Proviste Prénom Georges Henri Francis Gustave Alain Requêtes SELECT <col_i>,...,<col_k> FROM <nom_table> Sélectionne tous les tuples de la table Les tuples sont composés des colonnes spécifiées Requêtes Table ETUDIANT: Nom Prénom Dupont Georges Dupont Henri Durant Francis Durant Gustave Proviste Alain SELECT Nom,Prénom FROM ETUDIANTS Nom Dupont Dupont Durant Durant Proviste Prénom Georges Henri Francis Gustave Alain Age 17 16 17 17 14 Points 12 13 15 14 12 Requêtes SELECT DISTINCT * FROM <nom_table> DISTINCT permet de sélectionner tous les tuples différents de la table Requêtes Table ETUDIANT: Nom Prénom Dupont Georges Dupont Henri Durant Francis Durant Gustave Proviste Alain Age 17 16 17 17 14 SELECT DISTINCT Nom FROM ETUDIANTS Nom Dupont Durant Proviste Points 12 13 15 14 12 Requêtes SELECT COUNT * FROM <nom_table> COUNT compte le nombre de tuples de la table Requêtes Table ETUDIANT: Nom Prénom Dupont Georges Dupont Henri Durant Francis Durant Gustave Proviste Alain Age 17 16 17 17 14 Points 12 13 15 14 12 SELECT COUNT(DISTINCT Name) FROM ETUDIANTS 3 (Dupont, Durant, Proviste) Requêtes SELECT * FROM <nom_table> WHERE CSTR(column) WHERE impose des contraintes sur les tuples en fonction des valeurs des colonnes Requêtes Table ETUDIANT: Nom Prénom Dupont Georges Dupont Henri Durant Francis Durant Gustave Proviste Alain Age 17 16 17 17 14 Points 12 13 15 14 12 Age 17 16 Points 12 13 SELECT * FROM ETUDIANTS WHERE Nom="Dupont" Nom Dupont Dupont Prénom Georges Henri Requêtes SELECT * FROM <nom_table_1> NATURAL JOIN <nom_table_2> NATURAL JOIN est le produit cartésien entre deux tables basé sur les noms de colonnes communs Requêtes TABLE ETUDIANTS: ID 1 2 Nom Proviste Durant Prénom Alain Georges TABLE INTERROS: ID Cours Points 1 Math 17 1 Francais 10 2 Geographie 14 2 Francais 12 SELECT * FROM ETUDIANTS NATURAL JOIN INTERROS ID 1 1 2 2 Nom Proviste Proviste Durant Durant Prénom Alain Alain Georges Georges Cours Math Francais Geographie Francais Points 17 12 14 12 Requêtes TABLE ETUDIANTS: ID 1 2 Nom Proviste Durant Prénom Alain Georges TABLE INTERROS: ID Cours Points 1 Math 17 1 Francais 10 2 Geographie 14 2 Francais 12 SELECT DISTINCT Nom,Prenom FROM ETUDIANTS NATURAL JOIN INTERROS WHERE Points >= 14 Nom Proviste Durant Prénom Alain Georges Requêtes TABLE ETUDIANTS: ID 1 2 Nom Proviste Durant Prénom Alain Georges TABLE INTERROS: ID Cours Points 1 Math 17 1 Francais 10 2 Geographie 14 2 Francais 12 SELECT DISCTINCT NOM,PRENOM FROM ETUDIANTS WHERE ID IN (SELECT ID FROM INTERROS WHERE Points>=14) Nom Proviste Durant Prénom Alain Georges Requêtes SELECT * FROM <NOM_TABLE> GROUP BY <colonne> GROUP BY regroupe les résultats par une ou plusieurs colonnes Requêtes TABLE ETUDIANTS: Nom Prénom Dupont Georges Dupont Henri Durant Francis Durant Gustave Proviste Alain Age 17 16 17 17 14 Points 12 13 15 14 12 SELECT Nom, SUM(Age), AVG(Points) FROM ETUDIANTS GROUP BY Nom Nom Dupont Durant Proviste SUM(Age) 33 34 14 AVG(Points) 12.5 14.5 12 Exercice 1.2 Personne(n national, nom, prenom, sexe, rue, numero, CP, CL) Commune(code postal,nom commune) Localite(code postal,code localite,nom localite) Mariage(code mariage, n national mari, n national femme, date mariage, date divorce, code postal) Enfant(n national enfant, n national pere, n national mere, date naissance) (a) Nom et prénom des personnes s’étant mariées à Liège (b) Nom et prénom des personnes sans enfants (c) Nom et prénom des personnes nées de père inconnu (d) Nombre de localités par communes (e) Nom et prénom des enfants nés après le mariage de leurs parents (f) Pourcentage de divorces (a) Nom et prénom des personnes s’étant mariées à Liège SELECT DISTINCT nom, prenom FROM Personne WHERE n_national IN (SELECT n_national_mari FROM Mariage WHERE code_postal=4000) OR n_national IN (SELECT n_national_femme FROM Mariage WHERE code_postal=4000); (b) Nom et prénom des personnes sans enfants SELECT DISTINCT nom, prenom FROM Personne WHERE n_national NOT IN (SELECT nn FROM (SELECT n_national_pere AS nn FROM Enfant) UNION (SELECT n_national_mere AS nn FROM Enfant) ); (c) Nom et prénom des personnes nées de père inconnu SELECT DISTINCT nom, prenom FROM Personne WHERE n_national IN (SELECT n_national_enfant FROM Enfant WHERE n_national_pere IS NULL); (d) Nombre de localités par commune SELECT nom_commune, COUNT(code_localite) FROM Commune NATURAL JOIN Localite GROUP BY code_postal (e) Nom et prénom des enfants nés après le mariage de leurs parents (I) 1. Sélectionner le numéro national des enfants nés après le mariage de leurs parents SELECT n_national_enfant FROM Enfant e WHERE date_naissance > (SELECT date_mariage FROM Mariage WHERE n_national_mari = e.n_national_pere AND n_national_femme = e.n_national_mere) (e) Nom et prénom des enfants nés après le mariage de leurs parents (II) 2. Trouver les noms et prénoms des personnes dont on a obtenu le numéro national SELECT nom, prenom FROM Personne WHERE n_national IN (SELECT n_national_enfant FROM Enfant e WHERE date_naissance > (SELECT date_mariage FROM Mariage WHERE n_national_mari = e.n_national_pere AND n_national_femme = e.n_national_mere) ); (f) Pourcentage de divorces par commune (I) 1. Nombre de mariages par code postal SELECT code_postal, COUNT(code_mariage) AS nmariages FROM Mariage GROUP BY code_postal 2. Nombre de divorces par code postal SELECT code_postal, COUNT(code_mariage) AS ndivorces FROM Mariage WHERE date_divorce IS NOT NULL GROUP BY code_postal (f) Pourcentage de divorces par commune (II) 3. Nombre de mariages et de divorces par code postal SELECT code_postal, nmariages, ndivorces FROM (SELECT code_postal, COUNT(code_mariage) AS nmariages FROM Mariage GROUP BY code_postal) NATURAL JOIN (SELECT code_postal, COUNT(code_mariage) AS ndivorces FROM Mariage WHERE date_divorce IS NOT NULL GROUP BY code_postal) (f) Pourcentage de divorces par commune (III) 4. Pourcentage de divorces par commune SELECT nom_commune, ndivorces/nmariages FROM SELECT code_postal, nmariages, ndivorces FROM ( (SELECT code_postal, COUNT(code_mariage) AS nmariages FROM Mariage GROUP BY code_postal) NATURAL JOIN (SELECT code_postal, COUNT(code_mariage) AS ndivorces FROM Mariage WHERE date_divorce IS NOT NULL GROUP BY code_postal) ) NATURAL JOIN Commune WHERE nmariages>0 Exercice 2 Créer les tables suivantes: 1. Auteur(code auteur, nom, prenom) 2. Livre(n livre, ISBN, titre, editeur, date edition) 3. Ecrit(code auteur, n livre) 4. Exemplaire(n livre, n exemplaire, etat, date acq) 5. Emprunteur(n inscr, nom, prenom, adresse) 6. Emprunt(n livre, n exemplaire, n inscr, date empr) Auteur(code auteur, nom, prenom) CREATE TABLE IF NOT EXISTS Auteur( code_auteur INT PRIMARY KEY, nom VARCHAR(50) NOT NULL, prenom VARCHAR(50) NOT NULL )ENGINE=InnoDB; Livre(n livre, ISBN, titre, date edition) CREATE TABLE IF NOT EXISTS Livre( n_livre INT PRIMARY KEY, ISBN VARCHAR(17) NOT NULL UNIQUE, editeur VARCHAR(50) NOT NULL, titre VARCHAR(100) NOT NULL, date_edition DATE NOT NULL )ENGINE=InnoDB; Ecrit(code auteur, n livre) CREATE TABLE IF code_auteur n_livre INT PRIMARY KEY FOREIGN KEY FOREIGN KEY )ENGINE=InnoDB; NOT EXISTS Ecrit( INT NOT NULL, NOT NULL, (code_auteur,n_livre) code_auteur REFERENCES Auteur(code_auteur), n_livre REFERENCES Livre(n_livre) Exemplaire(n livre, n exemplaire, etat, date acq) CREATE TABLE IF NOT EXISTS Exemplaire( n_livre INT NOT NULL, n_exemplaire INT NOT NULL, etat VARCHAR(50) NOT NULL, date_acq DATE NOT NULL, PRIMARY KEY (n_livre,n_exemplaire) FOREIGN KEY n_livre REFERENCES Livre(n_livre) )ENGINE=InnoDB; Emprunteur(n inscr, nom, prenom, adresse) CREATE TABLE IF NOT EXISTS Emprunteur( n_inscr INT PRIMARY KEY, nom VARCHAR(50) NOT NULL, prenom VARCHAR(50) NOT NULL, adresse VARCHAR(200) NOT NULL, )ENGINE=InnoDB; Emprunt(n livre, n exemplaire, n inscr, date empr) CREATE TABLE IF NOT EXISTS Emprunt( n_livre INT NOT NULL, n_exemplaire INT NOT NULL, n_inscr INT NOT NULL, date_empr DATE NOT NULL, PRIMARY KEY (n_livre, n_exemplaire, n_inscr, date_empr), FOREIGN KEY n_livre REFERENCES Livre(n_livre), FOREIGN KEY (n_livre,n_exemplaire) REFERENCES Exemplaire(n_livre,n_exemplaire) , FOREIGN KEY n_inscr REFERENCES Emprunteur(n_inscr) )ENGINE=InnoDB; Exercice 2.2 (a) Titres écrits par Yves Fonctionrecurs (b) Numéros, noms et prénoms des emprunteurs qui ont en prêt un exemplaire du livre n◦ 10 (c) Nombre d’emprunts en cours (d) Pour chaque livre, numéros, titres et nombre d’exemplaires en prêt (e) Numéros et titres des livres édités depuis le 1er septembre 2003 (f) Noms, prénoms et codes des auteurs ayant écrit le plus de livres dans la bibliothèque (g) Numéros et titres des livre encore disponibles (il reste au moins un exemplaire) (h) Numéros et titres des livres dont tous les exemplaires ont été empruntés (i) Numéros et titres des livres disponibles édités après le 1er septembre 2003 dont le titre contient le mot motivation. (a) Titres écrits par Yves Fonctionrecurs SELECT titre FROM Auteurs NATURAL JOIN Ecrit NATURAL JOIN Livre WHERE Nom="Fonctionrecurs" AND prenom="Yves" (b) Numéro, nom et prénom des emprunteurs qui ont en prêt un exemplaire du livre n◦ 10 SELECT n_inscr, nom, prenom FROM Emprunt NATURAL JOIN Emprunteur WHERE n_livre=10 (c) Nombre d’emprunts en cours SELECT COUNT(*) FROM Emprunt (d) Pour chaque livre, numéro, titre et nombre d’exemplaires en prêt (I) Attention à ne pas oublier les livres qui n’ont pas été prêtés... 1. Numéro et nombre d’emprunt(s) des livres empruntés au moins une fois SELECT n_livre, COUNT(*) as n_empruntes FROM Emprunt GROUP BY n_livre 2. Numéro et nombre d’emprunt(s) des livres qui ne sont pas empruntés SELECT n_livre, 0 as n_empruntes FROM Livre WHERE n_livre NOT IN (SELECT n_livre FROM Emprunt) (d) Pour chaque livre, numéro, titre et nombre d’exemplaires en prêt (II) 3. Union de ces deux tables (SELECT n_livre, COUNT(*) as n_empruntes FROM Emprunt GROUP BY n_livre) UNION (SELECT n_livre, 0 as n_empruntes FROM Livre WHERE n_livre NOT IN (SELECT n_livre FROM Emprunt) (d) Pour chaque livre, numéro, titre et nombre d’exemplaires en prêt (III) 4. NATURAL JOIN avec Livre pour récupérer le titre SELECT n_livre, titre, n_empruntes FROM ((SELECT n_livre, COUNT(*) as n_empruntes FROM Emprunt GROUP BY n_livre) UNION (SELECT n_livre, 0 as n_empruntes FROM Livre WHERE n_livre NOT IN (SELECT n_livre FROM Emprunt)) NATURAL JOIN Livre (e) Numéros et titres des livres édités depuis le 1er septembre 2003 SELECT n_livre, titre FROM Livre WHERE date_edition>=’01-09-2003’ (f) Nom, prénom et code des auteurs ayant écrit le plus de livres dans la bibliothèque (I) 1. Trouver les livres présents en bibliothèque SELECT DISTINCT n_livre FROM Exemplaire 2. Obtenir le code auteur et le nombre correspondant de livres écrits en bibliothèque SELECT code_auteur, COUNT(n_livre) as cnt FROM (SELECT DISTINCT n_livre FROM Exemplaire NATURAL JOIN Ecrit) GROUP BY code_auteur I Appelons cette table T1 (f) Nom, prénom et code des auteurs ayant écrit le plus de livres dans la bibliothèque (II) 3. Trouver le maximum de livres écrits par un auteur dans la bibliothèque SELECT MAX(cnt) FROM T1 4. Rechercher les auteurs ayant au moins autant de livres... SELECT code_auteur, cnt FROM T1 HAVING cnt>= SELECT MAX(cnt) FROM T1 (f) Nom, prénom et code des auteurs ayant écrit le plus de livres dans la bibliothèque (III) 5. Requete finale SELECT nom, prenom, code_auteur FROM ((SELECT code_auteur, cnt FROM T1 HAVING cnt>= SELECT MAX(cnt) FROM T1) NATURAL JOIN Auteur) (g) Numéro et titre des livre encore disponible (il reste au moins un exemplaire) (I) 1. Rechercher si un exemplaire ex particulier a été emprunté SELECT * FROM Emprunt WHERE n_livre = ex.n_livre AND n_exemplaire = ex.n_exemplaire 2. Rechercher les identifiants de livre dont un exemplaire n’a pas été emprunté SELECT DISTINCT n_livre FROM Exemplaire ex WHERE NOT EXISTS SELECT * FROM Emprunt WHERE n_livre = ex.n_livre AND n_exemplaire = ex.n_exemplaire (g) Numéro et titre des livre encore disponible (il reste au moins un exemplaire) (II) 3. NATURAL JOIN avec Livre pour obtenir les titres SELECT titre, n_livre FROM ((SELECT n_livre FROM Exemplaire ex WHERE NOT EXISTS (SELECT * FROM Emprunt WHERE n_livre = ex.n_livre AND n_exemplaire = ex.n_exemplaire)) NATURAL JOIN Livre) (h) Numéro et titre des livres dont tous les exemplaires ont été empruntés (I) On cherche l’inverse de la question précédente! 1. Rechercher les identifiants de livres disponibles SELECT DISTINCT n_livre FROM Exemplaire ex WHERE NOT EXISTS SELECT * FROM Emprunt WHERE n_livre = ex.n_livre AND n_exemplaire = ex.n_exemplaire (h) Numéro et titre des livres dont tous les exemplaires ont été empruntés (II) 2. Chercher les livres qui ne sont pas présents dans cette table. SELECT titre, n_livre FROM Livre l WHERE NOT IN (SELECT DISTINCT n_livre FROM Exemplaire ex WHERE ex.n_livre = l.n_livre AND NOT EXISTS (SELECT * FROM Emprunt WHERE n_livre = ex.n_livre AND n_exemplaire = ex.n_exemplaire)) (i) Numéro et titre des livres disponibles édités après le 1er septembre 2003 dont le titre contient le mot motivation. Encore très semblable à (g)... SELECT titre, n_livre FROM ((SELECT n_livre FROM Exemplaire ex WHERE NOT EXISTS (SELECT * FROM Emprunt WHERE n_livre = ex.n_livre AND n_exemplaire = ex.n_exemplaire)) NATURAL JOIN Livre) WHERE date_edition>’01-09-2003’ AND titre LIKE ’%motivation%’