Copyright © 2010 D.Gonzalez
mardi 7 septembre 2010 à 10h21
Table des matières
SELECT, syntaxe de baseSELECTListe des tableaux
UNDEUXListe des exemples
TO_CHAR (numérique)TO_CHAR (caractères)TO_CHAR (suffixe)Table des matières
Ces pages ont pour origine un cours destiné aux étudiants de 2ème année de l'IUP IIES de l'université de Lille III-Charles de Gaulle, à Villeneuve d'Ascq, pour les années universitaires 2002-2003 et 2003-2004.
Elles ont ensuite été remaniées et augmentées pour un cours destiné aux étudiants de 3ème année de la licence MIASHS.
Ces pages ne sont pas destinées à être un cours autonome : elles ne sont qu'un support de cours, et beaucoup de choses, qui sont transmises à l'oral pendant les cours, ne sont pas écrites.
L'environnement technique du cours est constitué de machines sous linux, les bases de données étant hébergées sur un serveur PostgreSQL, lui aussi sous linux. L'installation de ces logiciels ne sera pas abordée et ne fait pas partie du contenu du cours.
On n'abordera pas non plus la partie analyse (Merise, UML, etc.) qui est pourtant fondamentale dans le processus de création d'une base données. Cette partie sera vue ailleurs. Ce cours se limite à la découverte de SQL.
Les petites images animées qui illustrent les liens de la version web de ce document proviennent de QBullets
Table des matières
Ce premier chapitre est extrait d'un polycopié réalisé par Rémi Gilleron et Marc Tommasi dans le cadre de leur cours sur l' architecture client-serveur.
Je me suis contenté d'en adapter la mise en page.
Vous pouvez consulter l'original sur internet.
Ce polycopié était lui-même fortement inspiré de l'excellent ouvrage « SQL 2 », de Christian Marée et Guy Ledant, chez Armand Colin.
Tous les systèmes de gestion de données utilisent SQL pour l'accès aux données ou pour communiquer avec un serveur de données. SQL (Standard Query Language) est né à la suite des travaux mathématiques de Codd, travaux qui ont fondé les bases de données relationnelles. SQL, défini d'abord chez IBM, a subi trois tentatives de normalisation en 86, 89 et 92 (SQL 2 ou SQL 92). Nous présentons trois raisons fondamentales qui justifient l'utilisation de SQL.
D'une part, la structuration et la manipulation des données sont devenues très complexes. Pour une application de taille moyenne, la base de données contient fréquemment plus de trente tables fortement interconnectées. Il est donc hors de question de manipuler les données de façon algorithmique traditionnelle. Une requête SQL dans un langage logique simple remplace donc bien avantageusement plusieurs dizaines de lignes d'un langage de programmation tel C ou Cobol.
D'autre part, l'architecture client-serveur est omniprésente. Tandis que la station client exécute le code de l'application en gérant, en particulier, l'interface graphique, le serveur optimise la manipulation et le contrôle des données. De plus, les applications doivent être portables et gérer des données virtuelles, c'est-à-dire émanant de n'importe quel serveur. Développer une application dans un environnement hétérogène n'est possible que parce que la communication entre l'applicatif client et le serveur est réalisée par des primitives SQL normalisées.
Enfin, les applications à développer (même sur un PC) sont devenues de plus en plus complexes. Le profil du programmeur a fortement changé. Il doit maintenant traiter des données de plus en plus volumineuses, intégrer les techniques de manipulation des interfaces, maîtriser la logique événementielle et la programmation orientée objet, tout celà dans un contexte d'architecture client-serveur où se cotoient les systèmes d'exploitation et les protocoles de réseaux hétérogènes. L'accès et la manipulation des données ne sont que l'un des aspects de la conception et de la réalisation de programmes. On cherche donc à acquérir un environnement de développement performant qui prend en charge un grand nombre de tâches annexes. Des outils de développement sont apparus pour permettre au développeur de se concentrer sur l'application proprement dite : générateurs d'écrans, de rapports, de requêtes, d'aide à la conception de programme, de connection à des bases de données distantes via des réseaux. Dans tous ces outils, la simplicité et la standardisation de SQL font que SQL est utilisé chaque fois qu'une définition, une manipulation, ou un contrôle de données est nécessaire. SQL est donc un élément central entre les divers composants d'un environnement de développement dans une architecture client-serveur.
Dans la phase d'analyse de systèmes d'information, on considère différents niveaux d'abstraction du système d'information : le niveau conceptuel, le niveau logique ou organisationnel et enfin, le niveau physique ou opérationnel.
Nous allons considérer ici différents niveaux de perception d'une base de données relationnelle. Un SGBD est fréquemment décrit par une structure en couches correspondant à des perceptions différentes des données, associées à des tâches différentes pour différents acteurs.
Pour plus de simplicité, nous distinguerons trois types d'acteurs : les administrateurs de la base de donnée, les développeurs et les utilisateurs. Au niveau externe, proche de l'utilisateur, la perception est totalement indépendante du matériel et des techniques mises en oeuvre, tandis qu'au niveau le plus intérieur, se trouvent les détails de l'organisation sur disque et en mémoire.
Le schéma logique est l'ensemble de toutes les données pertinentes, toutes applications confondues. Il est rendu conforme à un modèle de représentation des données, et est totalement indépendant de la technologie utilisée. Nous choisissons le modèle relationnel. Ce niveau a un inconvénient : toutes les données sont accessibles à tout le monde. Cet ensemble vaste de données est trop touffu. Il est préférable de montrer à l'utilisateur (et au programmeur) une vue plus simple des données.
On constitue ainsi des schémas externes. Par exemple, le gestionnaire du stock n'est concerné que par les données décrivant les articles en stock. S'il ne manipule que des bordereaux d'entrée, des bordereaux de sortie, et des fiches d'état du stock, ceux-ci constituent son schéma de perception externe.
Le schéma interne fournit une perception plus technique des données.
Enfin le schéma physique est dépendant du matériel et du logiciel de base.
- Niveau externe
Au niveau externe, les utilisateurs et développeurs d'application ont une perception limitée de la base de données. On parle de vue. Une vue peut être considérée comme une restriction du schéma logique à un type d'utilisateur. Ce niveau concerne les utilisateurs et les développeurs.
- Niveau logique
Traduction dans le modèle relationnel du schéma conceptuel. On précise à ce niveau les tables, les relations entre tables, les contraintes d'intégrité, les vues et les droits par groupe d'utilisateurs. Ce niveau concerne l'administrateur et les développeurs.
- Niveau interne
On définit les index et tous les éléments informatiques susceptibles d'optimiser les ressources et les accès aux données. Ce niveau concerne l'administrateur.
- Niveau physique
On y précise tout ce qui dépend du matériel et du système d'exploitation. Ce niveau concerne l'administrateur.
Cette découpe en niveaux présente les avantages suivants :
Les applications développées sont indépendantes du niveau interne. Tout changement de stratégie d'accès, ou d'organisation des données entraîne une modification au niveau interne, mais le schéma logique reste inchangé. Par exemple, une requête SQL précise le QUOI sans se préoccuper du COMMENT.
La distinction externe/logique assure (en partie) l'indépendance entre les applications et le niveau logique. Par exemple on peut enrichir le schéma logique sans modifier les applications existantes pour toutes les vues non concernées par les modifications apportées au schéma logique.
La distinction logique/interne permet de modifier les optimisations d'accès aux données. Par exemple, si une application a des performances insuffisantes, il est possible d'optimiser les accès (en introduisant de nouveaux index, par exemple) et d'augmenter les performances sans modifier l'application.
La distinction interne/physique permet une meilleure portabilité car seule la partie physique est dépendante du matériel et du système d'exploitation.
Le terme relationnel provient de la définition mathématique d'algèbre relationnelle (Codd 70). Une relation est un ensemble de tuples (tuple est la généralisation de couple à un nombre quelconque d'éléments. Les couples, les triplets, les quadrupets, etc. sont des tuples) de données, on peut alors définir des opérations algébriques sur les relations (voir la section la section intitulée « SQL : un langage algébrique »). Nous parlerons dans la suite de table, et non pas de relation. Une table est un ensemble de tuples de données distincts deux à deux. Une table est constituée d'une clef primaire et de plusieurs attributs (ou colonnes) qui dépendent de cette clef. La clef primaire d'une table est un attribut ou un groupe d'attributs de la table qui détermine tous les autres de façon unique. Une table possède toujours une et une seule clef primaire. Par contre, une table peut présenter plusieurs clefs candidates qui pourraient jouer ce rôle. Le domaine d'un attribut est l'ensemble des valeurs que peut prendre cet attribut. Le domaine est constitué d'un type, d'une longueur et de contraintes qui réduisent l'ensemble des valeurs permises. Une clef étrangère dans une table est une clef primaire ou candidate dans une autre table. Les contraintes d'intégrité font partie du schéma logique. Parmi celles-ci, on distingue :
les contraintes de domaine qui restreignent l'ensemble des valeurs que peut prendre un attribut dans une table,
les contraintes d'intégrité d'entité qui précisent qu'une table doit toujours avoir une clef primaire et
les contraintes d'intégrité référentielle qui précisent les conditions dans lesquelles peuvent être ajoutés ou supprimés des enregistrements lorsqu'il existe des associations entre tables par l'intermédiaire de clefs étrangères.
Exemple 2.1. Contraintes d'intégrité
clients (cltnum, cltnom, cltpnom, cltloc, cltca, clttype) commandes (cmdnum, cmdclt, cmddate, cmdvnd) ligcommandes (ldccmd, ldcart, ldcqte) articles (artnum, artnom, artpv, artcoul)Les identifiants sont en gras, les clefs étrangères en italiques. Une contrainte d'intégrité référentielle est, par exemple, l'obligation de la présence d'un client pour une comande. C'est-à-dire encore qu'à un enregistrement dans la table
commandesdoit correspondre un enregistrement de la tableclientstel quecommandes.cmdclt=clients.cltnum.
Nous allons d'abord voir les différents types d'application possibles pour la gestion de données distantes en commençant par les trois formes les plus simples.
- Monoposte
La base de données se trouve sur un poste et n'est pas accessible en réseau. Il faut, dans ce cas, penser à la notion de sécurité si plusieurs utilisateurs peuvent interroger la base (suppression accidentelle d'enregistrements).
- Multiposte, basée sur des terminaux liés à un site central
C'est l'informatique multiposte traditionnelle. La gestion des données est centralisée. Les applications ont été écrites par le service informatique et seules ces applications peuvent interroger le serveur.
- Multiposte, basée sur un serveur de fichiers
C'est la première forme (la plus simple) d'architecture client-serveur. Si l'applicatif sur un PC souhaite visualiser la liste des clients habitant Paris, tous les enregistrements du fichier CLIENT transitent sur le réseau, entre le serveur et le client, la sélection (des clients habitant Paris) est faite sur le PC. Le trafic sur le réseau est énorme et les performances se dégradent lorsque le nombre de clients augmente. Les serveurs de fichiers restent très utilisés comme serveurs d'images, de documents, d'archives.
De nouveaux besoins sont apparus :
diminuer le trafic sur le réseau pour augmenter le nombre de postes sans nuire au fonctionnement,
traiter des volumes de données de plus en plus grand,
accéder de façon transparente à des données situées sur des serveurs différents,
accéder aux données de façon ensembliste, même si les données sont distantes, afin de diminuer le travail du programmeur,
adopter des interfaces graphiques de type Windows pour les applicatifs clients.
Dans une architecture client-serveur, un applicatif est constitué de trois parties : l'interface utilisateur, la logique des traitements et la gestion des données. Le client n'exécute que l'interface utilisateur et la logique des traitements, laissant au serveur de bases de données la gestion complète des manipulations de données.
Le serveur de bases de données fournit des services aux processus clients. Les tâches qu'il doit prendre en compte sont : la gestion d'une mémoire cache, l'exécution de requêtes exprimées en SQL, exécuter des requêtes mémorisées, la gestion des transactions, la sécurité des données.
Le client doit ouvrir une connection pour pouvoir profiter des services du serveur. Il peut ouvrir plusieurs connections simultanées sur plusieurs serveurs. Le client peut soumettre plusieurs requêtes simultanément au serveur et traiter les résultats de façon asynchrone.
Communication entre le client et le serveur. Puisque l'application doit pouvoir se connecter à divers serveurs de façon transparente, le langage de communication SQL doit être compatible avec la syntaxe SQL de chaque serveur pressenti. Or, malgré les normes, les dialectes SQL sont nombreux et parfois source d'incompatibilité. La seule façon de permettre une communication plus large est d'adopter un langage SQL standardisé de communication. Une couche fonctionnelle du client traduit les requêtes du dialecte SQL client en SQL normalisé. La requête transformée est envoyée au serveur. Celui-ci traduit la requête dans le dialecte SQL-serveur et l'exécute. Le résultat de la requête suit le chemin inverse. Le langage de communication normalisé le plus fréquent est l'ODBC (Open DataBase Connectivity) de Microsoft. Signalons également IDAPI (Integrated Database Application Programming Interface) de Borland. De plus, ces programmes permettent d'interroger des bases de données autres que relationnelles.
Une transaction correspond à une procédure SQL, i.e. un groupe d'instructions SQL. Il y a un seul échange requête/réponse pour la transaction. Le succès ou l'échec concerne l'ensemble des instructions SQL. Ces serveurs sont essentiellement utilisés pour l'informatique de production (ou opérationnelle) pour laquelle la rapidité des temps de réponse est importante sinon essentielle.
Les instructions essentielles SQL se répartissent en trois familles fonctionnellement distinctes et trois formes d'utilisation :
Selon la norme SQL 92, le SQL interactif permet d'exécuter une requête et d'en obtenir immédiatement une réponse. Le SQL intégré ( ou module SQL) permet d'utiliser SQL dans un langage de troisième génération (C, Cobol, ...), les instructions permettant essentiellement de gérer les curseurs. Le SQL dynamique est une extension du SQL intégré qui permet d'exécuter des requêtes SQL non connues au moment de la compilation. Hors norme, SQL est utilisable sous la forme de librairies de fonctions API (exemple : ODBC). Nous nous limitons au SQL interactif.
Dans le SQL interactif, le LDD (Langage de Définition de données) permet la description de la structure de la base (tables, vues, index, attributs, ...). Le dictionnaire contient à tout moment le descriptif complet de la structure de données. Le LMD (Langage de Manipulation de Données) permet la manipulation des tables et des vues. Le LCD (Langage de Contrôle des Données) contient les primitives de gestion des transactions et des privilèges d'accès aux données. Le tableau ci-dessous vous donne les principales primitives SQL et leur classification. Nous nous intéresserons essentiellement au LMD et à la commande SELECT. Nous étudierons également quelques problèmes concernant les privilèges d'accès et les transactions.
Tableau 2.1. SQL interactif
LDD LMD LCD create select grant drop insert revoke alter delete connect update commit rollback set
Pour bien comprendre le langage SQL, nous allons brièvement exposer les principes sur lesquels repose ce langage (algèbre relationnelle).
Une table (relation) est un ensemble de tuples. On peut donc appliquer à une table les opérateurs algébriques usuels. Le résultat d'une opération ou requête est une nouvelle table qui est exploitable à son tour dans une nouvelle opération. Tous les opérateurs peuvent être dérivés de cinq primitives de base : la projection, la sélection, l'union, la différence et le produit. L'opérateur de jointure qui peut être déduit des cinq primitives de base est cependant fondamental (algèbre relationnelle).
La projection permet de ne conserver que les attributs intéressants d'une table (sélection verticale). De plus, la projection élimine les répétitions de tuples résultant de cette sélection.
La sélection permet de ne conserver que les tuples qui respectent une condition définie sur les valeurs des attributs (sélection horizontale).
L'union réalise l'union de plusieurs tables.
Exemple 2.4. Union
CLIENTS3 = SELECT CLIENTS WHERE cltloc = 'PARIS' UNION SELECT BONSCLIENTS WHERE cltloc='BRUXELLES'
La différence consiste à prendre les tuples appartenant à une table mais pas à une autre.
Le produit réalise la juxtaposition de tous les tuples de la première table avec chaque tuple de la seconde. Celà signifie que, si les deux tables ont respectivement M et N tuples, la table résultante aura M×N tuples. Cette opération présente peu d'intérêt mais combinée avec une sélection, on obtient une opération fondamentale : la jointure.
La jointure n'est possible que sur deux tables possédant un attribut de domaine commun. Elle consiste à juxtaposer les tuples dont la valeur d'un attribut est égal dans les deux tables. C'est une primitive dérivée car elle peut être définie à l'aide des primitives précédentes (exercice laissé au lecteur).
Les primitives peuvent être combinées pour constituer des requêtes plus élaborées. La séquence d'opérateurs permettant de réaliser une requête élaborée devient assez vite complexe. Le langage SQL permet (heureusement) d'exprimer globalement une requête sans faire apparaître les tables et les primitives intermédiaires. Ce sera le moteur SQL qui sera chargé d'optimiser la requête.
Exemple 2.7. Requête
Une requête SQL qui permet de dresser la liste des noms des clients qui ont acheté des articles de moins de 200F est :
SELECT DISTINCT cltnom FROM clients, commandes, ligcommandes, articles WHERE cltnum=cmdclt AND artnum=lcdart AND cmdnum=ldccmd AND artpv < 200Une combinaison de primitives permettant d'exécuter cette requête est :
temp1 = SELECT articles WHERE artpv < 200 temp2 = PROJECT temp1 OVER (artnum) temp3 = PROJECT clients OVER (cltnum,cltnom) temp4 = PROJECT commandes OVER (cmdnum, cmdclt) temp5 = PROJECT ligcommandes OVER (ldccmd,ldcart) temp6 = temp2 JOIN temp5 ON artnum=ldcart temp7 = PROJECT temp6 OVER (ldccmd) temp8 = temp3 JOIN temp4 ON cltnum=cmdclt temp9 = PROJECT temp8 OVER (cltnom, cmdnum) temp10 = temp7 JOIN temp9 ON ldccmd=cmdnum resultat = PROJECT temp10 OVER (cltnom)Une telle séquence n'est, en général, pas unique. La séquence fournie est une des plus efficaces (le lecteur peut s'exercer à en trouver d'autres).
Table des matières
Vous trouverez les réponses des exercices au Chapitre 11, Réponses aux premiers exercices sur la base jouet.
Calculer à la main le résultat des requêtes suivantes.
Table des matières
À partir de maintenant, vous allez untiliser une base de données qui gére le contenu d'une discothèque. Vous manipulerez des informations comme le nom des disques, leurs années de parution, les artistes, les textes des chansons, etc.
Liste des disques.
Liste des chansons pour chaque disque.
Un artiste/groupe pour chaque disque, mais possibilité de préciser des artistes/groupes particuliers pour chaque chanson (compilations, duos, etc.).
Genres musicaux, langue(s) pour chaque artiste/groupe, langue(s) pour chaque chanson.
Auteur(s) des chansons.
Paroles des chansons.
Appartenance(s) d'un artiste à un groupe.
Obtenir une liste des disques (avec les caractéristiques) et la liste des chansons.
Obtenir une liste des artistes.
Rechercher une chanson.
Quelques statistiques.
Consultations des paroles des chansons.
Insertion d'information dans la base (créer un artiste, un genre musical, un disque, une chanson, etc.).
Gérer les utilisateurs et leurs droits (créer, modifier, supprimer).
Modifier ou supprimer des données existantes (paroles de chanson, orthographe des noms, etc.).
Table des matières
L'analyse ne sera pas présentée ici. Nous nous contenterons de donner la structure de la base.
Structures et quelques exemples des données figurant dans les différentes tables de la base Disques.
Dans toutes les tables suivantes les champs dont le nom se termine par « _modif » contiennent la date de dernière modification de l'enregistrement.
La table ARTISTE contient les artistes utilisés dans la base (interprètes, compositeurs, etc.). Il peut s'agir de personnes, mais aussi de groupes. Les membres du groupes sont identifiés alors grâce à la table MEMBRE.
CREATE TABLE artiste (
art_num INTEGER NOT NULL PRIMARY KEY,
art_prenom CHARACTER VARYING,
art_nom CHARACTER VARYING,
art_groupe CHARACTER VARYING,
art_modif TIMESTAMP WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP);La table MEMBRE sert à définir une relation d'appartenance entre un artiste au sens propre dans la table ARTISTE et un groupe également dans la table ARTISTE. Figurent aussi dans cette table les début et fin des apparternances d'un musicien à un groupe. On peut ainsi répertorier plusieurs appartenances successives d'une même personne à un même groupe.
CREATE TABLE membre (
mem_membre INTEGER NOT NULL REFERENCES artiste(art_num),
mem_groupe INTEGER NOT NULL REFERENCES artiste(art_num),
mem_debut CHARACTER VARYING NOT NULL,
mem_fin CHARACTER VARYING NOT NULL,
PRIMARY KEY (mem_membre, mem_groupe, mem_debut, mem_fin));Chaque enregistrement correspond à un disque. Champ dis_cddb correspond à l'identifiant CDDB ou freedb du disque. Les champs dis_artiste et dis_etat font respectivement références aux tables ARTISTE et ETAT.
CREATE TABLE disque (
dis_num INTEGER NOT NULL PRIMARY KEY,
dis_titre CHARACTER VARYING,
dis_artiste INTEGER REFERENCES artiste(art_num),
dis_annee CHARACTER VARYING,
dis_cddb CHARACTER VARYING,
dis_anneeachat CHARACTER VARYING,
dis_prixachat NUMERIC(8,2),
dis_etat INTEGER REFERENCES etat(eta_num),
dis_perdu BOOLEAN DEFAULT false,
dis_modif TIMESTAMP WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP);Tableau 5.3.
| Champ | Enreg. n°16 | Enreg. n°67 | Enreg. n°49 |
|---|---|---|---|
| dis_num | 840 | 913 | 893 |
| dis_titre | Les Bons Morceaux | Diabolo Menthe | Dirty Deeds Done Dirt Cheap |
| dis_artiste | 283 | 556 | 4 |
| dis_annee | 1994 | 1979 | 1976 |
| dis_cddb | f20d3712 | 5f097909 | |
| dis_anneeachat | 1996 | 2009 | 2001 |
| dis_prixachat | 13.89 | 10.54 | 16.04 |
| dis_etat | 3 | 2 | 1 |
| dis_perdu | 1 | ||
| dis_modif | 2009-05-29 08:32:27.301759 | 2009-12-29 10:33:36.441709 | 2009-03-27 17:11:41.340889 |
Table des genres musicaux disponibles.
CREATE TABLE genre (
gen_num INTEGER NOT NULL PRIMARY KEY,
gen_nom CHARACTER VARYING,
gen_modif TIMESTAMP WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP);Table des états possibles pour un disque.
CREATE TABLE etat (
eta_num INTEGER NOT NULL PRIMARY KEY,
eta_nom CHARACTER VARYING);Une fiche par chanson. On parle bien de la chanson en tant qu'œuvre, et non d'un enregistrement donné par un artiste donné. Si une chanson a été enregistrée en plusieurs versions, elle ne figurera qu'une fois. Ce sera à la table INTERPRETE de faire le lien entre la chanson et toutes ses interprétations.
La champ booléen cha_libre indique si les paroles de la chanson sont libres de droits.
CREATE TABLE chanson (
cha_num INTEGER NOT NULL PRIMARY KEY,
cha_titre CHARACTER VARYING,
cha_genre INTEGER REFERENCES genre(gen_num),
cha_texte CHARACTER VARYING,
cha_libre BOOLEAN,
cha_modif TIMESTAMP WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP);Tableau 5.6.
| Champ | Enreg. n°1 | Enreg. n°16 | Enreg. n°128 |
|---|---|---|---|
| cha_num | 11712 | 606 | 289 |
| cha_titre | La nuit, les désirs | Menuetto - allegretto | The Water Is Wide |
| cha_genre | 5 | 1 | 2 |
| cha_texte | la nuit les désirs s'enr[...] | The water is wide an'[...] | |
| cha_libre | |||
| cha_modif | 2009-12-07 00:09:33.161239 | 2008-09-10 00:06:47.871009 | 2009-11-24 19:57:39.458133 |
Table des langues disponibles.
CREATE TABLE langue (
lan_num CHARACTER VARYING NOT NULL PRIMARY KEY,
lan_nom CHARACTER VARYING);Table dont chaque enregistrement fait la liaison entre un artiste et une langue.
CREATE TABLE langueartiste (
laa_langue CHARACTER VARYING NOT NULL REFERENCES langue(lan_num),
laa_artiste INTEGER NOT NULL REFERENCES artiste(art_num),
laa_maternelle BOOLEAN DEFAULT true,
PRIMARY KEY (laa_langue, laa_artiste));Table faisant la liaison entre une langue et une chanson.
CREATE TABLE languechanson (
lac_langue CHARACTER VARYING NOT NULL REFERENCES langue(lan_num),
lac_chanson INTEGER NOT NULL REFERENCES chanson(cha_num),
lac_principale BOOLEAN DEFAULT true NOT NULL,
PRIMARY KEY (lac_langue, lac_chanson, lac_principale));Table indiquant quel artiste interpréte quelle chanson. Le champ int_numero indique la place de la chanson sur le disque. C'est grâce à ce champ qu'on pourra établir le contenu ordonné d'un disque.
CREATE TABLE interprete (
int_artiste INTEGER NOT NULL (int_artiste) REFERENCES artiste(art_num),
int_chanson INTEGER NOT NULL REFERENCES chanson(cha_num),
int_disque INTEGER NOT NULL REFERENCES disque(dis_num),
int_numero INTEGER NOT NULL,
PRIMARY KEY (int_artiste, int_chanson, int_disque, int_numero));Table indiquant quel artiste a écrit quelle chanson. On ne préoccupe pas de distinguer compositeur et parolier.
CREATE TABLE ecrit (
ecr_artiste INTEGER NOT NULL REFERENCES artiste(art_num),
ecr_chanson INTEGER NOT NULL REFERENCES chanson(cha_num),
PRIMARY KEY (ecr_artiste, ecr_chanson)
);Cette table stocke les différents niveaux d'autorisations des utilisateurs de la base. Elle est référencée par la table AUTORISATIONS. Ces deux tables n'ont aucun lien avec les autres.
CREATE TABLE droits (
dro_num INTEGER NOT NULL PRIMARY KEY,
dro_nom CHARACTER VARYING,
dro_niveau INTEGER);Cette table stocke les identifications des utilisateurs autorisés à consulter la base (en lien avec un programme chargé de contrôler les accès), ainsi que leurs niveaux de droits, comme défini dans la table DROITS.
CREATE TABLE autorisations (
aut_login CHARACTER VARYING NOT NULL PRIMARY KEY,
aut_motdepasse CHARACTER VARYING,
aut_nom CHARACTER VARYING,
aut_prenom CHARACTER VARYING,
aut_droits INTEGER REFERENCES droits(dro_num));Table des matières
Vous trouverez les réponses des exercices au Chapitre 12, Réponses aux premiers exercices sur la commande
SELECT.
La commande de recherche est le verbe SELECT .
L'étude de la commande SELECT et des différentes clauses et fonctions est faite, pour commencer, en considérant une seule table.
Par la suite, l'utilisation de plusieurs tables dans le même SELECT, sera prise en compte.
La syntaxe de base est :
SELECT colonnes d'une ou plusieurs tables séparées par « , »
FROM tables séparées par « , »
WHERE conditions logiques séparées par « AND » ou « OR »
ORDER BY colonnes séparées par « , »
Le caractère « * » permet de demander l'affichage de toutes les colonnes d'une table.
A la place de « * » on peut donner la liste des colonnes souhaitées, dans l'ordre souhaité, en écrivant la requête sur une ou plusieurs lignes. Il s'agit de la projection vue précédemment. L'écriture sur plusieurs lignes est conseillée en vue de rendre plus simple la relecture et la modification de la requête.
Le titre des colonnes obtenues par un SELECT sont les noms des champs.
On peut cependant changer ces titres en utilisant AS. La commande
SELECT aut_login AS identifiant FROM autorisations ;produira le même effet que
SELECT aut_login FROM autorisations ;
à la différence que la colonne ne sera pas intitulé aut_login mais identifiant.
Si vous voulez utiliser un titre de colonne qui contient autre chose que des lettres non accentuées minuscules (des espaces, des ponctuations, des accents, des majuscules, etc.) il faut l'entourer par des guillemets (« " »). On écrira ainsi :
SELECT art_nom AS "Nom de l'artiste" FROM artiste ;
Il peut être utile de supprimer les doublons, d'où utilisation de la clause DISTINCT. Elle a pour effet de n'afficher qu'une seule fois les lignes qui seraient semblables à l'affichage.
On l'utilise sous la forme SELECT DISTINCT ....
Les clauses de restriction s'écrivent derrière WHERE. Elle permettent de sélectionner les lignes à afficher. Il s'agit de la sélection telle que vue précédemment.
Afficher le titre des disques de 1969 dont le code d'état est 2.
Afficher le nom et l'identifiant des genres musicaux dont l'identifiant est supérieur à 5.
Afficher le titre, le prix d'achat et le numéro CDDB des disques de l'année 1981, l'état est 2 et le prix d'achat est supérieur à 15.
Afficher le titre, l'identifiant CDDB et l'année des disques dont le titre est « IV » ou « Killer » ou « No Control ».
Afficher le titre, l'identifiant CDDB et l'année des disques de l'année 1971, dont le titre est « IV » ou « Killer ».
Afficher le titre, l'identifiant CDDB et l'année des disques dont le titre est « IV » ou dont le titre est « Killer » dans l'année 1971.
Afficher le titre, l'identifiant CDDB et le prix des disques dont le prix est compris entre 13.5 et 14.
Afficher le nom et le prénom des artistes dont le prénom appartient à la liste : « Alain, Louis, Jean ».
La restriction peut mettre en jeu la comparaison de deux ou plusieurs colonnes entre elles. Il suffit de les appeler par leurs noms.
Les opérateurs arithmétiques sont niés avec « ! » : par exemple « non égal » s'écrit « != » (ou « <> »).
Les autres opérateurs sont niés par « NOT » : par exemple « pas dans » s'écrit « NOT IN ».
Les caractères jokers sont « % » pour une chaîne et « _ » pour un caractère. On les utilise avec l'opérateur LIKE.
On peut trier (ordonner) l'affichage du résultat selon une ou plusieurs colonnes, voire selon des calculs faits sur les colonnes. Ceci sera réalisé grâce à ORDER BY.
En SQL il existe une valeur correspondant à la valeur vide. C'est l'équivalent de la valeur Null de certains langages de programmation (comme java ou python). C'est la valeur qu'ont les champs non renseignés.
Si on veut utiliser les valeurs non renseignées dans une restriction ce sera avec les clauses IS NULL ou IS NOT NULL.
Afficher les artistes dont le nom commence par la lettre « A » dont la date de modification n'est pas renseignée.
Afficher les artistes dont le nom commence par la lettre « A » dont la date de modification est renseignée.
Afficher les artistes dont le nom commence par la lettre « A » dont la dernière modification a été faite après le 1e janvier 2009.
Afficher les artistes dont le nom commence par la lettre « A » dont la dernière modification a été faite avant le 1e janvier 2009.
Des expressions arithmétiques[1] peuvent être utilisées après : SELECT, WHERE, et ORDER BY.
Le remplacement automatique des valeurs non renseignées (NULL) se fait par l'utilisation de la fonction COALESCE.
COALESCE( arg1, arg2, ...)
où :
arg1 = colonne qui peut être non renseignée
arg2 = valeur ou nom d'une autre colonne de substitution
...
Le résultat renvoyé est la première valeur non NULL.
La fonction ROUND(arg1, arg2) permet de faire l'arrondi mathématique de arg1 avec arg2 décimales.
La fonction TRUNC(arg1, arg2) permet de tronquer arg1 avec arg2 décimales.
Refaire l'exercice 26 mais en arrondissant à deux décimales les prix en francs. Renommer les colonnes.
La fonction de codification du SQL standard n'existe pas en PostgreSQL :
DECODE (arg1, arg20,arg21, arg30,arg31, arg40)
Mais on dispose de la structure CASE qui donne le même résultat (et avec plus de possibilités) :
CASE WHEN cond1 THEN arg1 WHEN cond2 THEN arg2 ... ELSE arg3 END
si la condition cond1 est vérifiée, alors le résultat est arg1,
sinon, si la condition cond2 est vérifiée, alors le résultat est arg2,
...
sinon le résultat est arg3.
La fonction extraction de chaîne de caractères SUBSTR(arg1, arg2, arg3) permet d'extraire de la chaîne arg1 à partir de la position arg2, les arg3 caractères suivants. Si arg3 n'est pas précisé, on obtient toute la fin de la chaîne de caractères.
La fonction de retour du rang d'une chaîne dans une autre chaîne, STRPOS(arg1, arg2) permet de retourner le rang de la chaîne arg2 dans la chaîne arg1.
Les fonctions UPPER(arg1) et LOWER(arg1) permettent respectivement de forcer à la majuscule ou à la minuscule.
La fonction LENGTH(arg1) permet d'obtenir le nombre de caractères d'une chaîne de caractères.
La fonction TRIM(arg1) supprime les espaces en début et fin de la chaîne.
Afficher le nom et le rang de la lettre « r » à partir de la 3ème lettre dans le nom des artistes.
Afficher le nom, le prénom, le prénom en majuscules et le prénom en minuscules des artistes dont le nom est Russell.
Afficher le nom et le nombre de caractères du nom des artistes.
Afficher proprement les noms complets des artistes (prénom+nom+groupe), le nom de famille étant en majuscules.
Table des matières
Vous trouverez les réponses des exercices au Chapitre 13, Réponses aux exercices sur les jointures et les sous-requêtes.
Dans la table DISQUES la colonne dis_artiste contient des valeurs qui se trouvent dans la colonne art_num de la table ARTISTE et inversement.
Logiquement chaque ligne de DISQUE correspond à une ligne de ARTISTE et chaque ligne ARTISTE correspond à une ou plusieurs lignes de DISQUE.
Cette correspondance se fait par l'opérateur égalité (« = ») entre les valeurs des deux colonnes des deux tables : c'est une équi-jointure.
Après FROM les deux tables sont citées, et le critère d'équi-jointure sert de restriction. Sans cette restriction c'est un produit cartésien de ARTISTE et de DISQUE qui serait obtenu.
Pour faciliter l'écriture des requêtes, les tables citées derrière FROM peuvent être renommées temporairement, et l'alias peut être utilisé pour préfixer les colonnes.
Si le critère de jointure est omis, le résultat est un produit cartésien inutilisable le plus souvent.
La possibilité de renommer temporairement une table dans une requête permet de faire la jointure d'une table sur elle-même, c'est à dire l'auto-jointure.
Afficher tous les couples possibles de disques qui sont du même artiste. (Un couple ne peut pas être constitué d'un seul disque...)
Idem, mais en écrivant chaque couple une seule fois (c'est-à-dire que si (a,b) est présent, (b,a) n'y sera pas).
Rechercher les artistes dont le nom complet (prénom+nom+groupe) est plus long que celui du groupe auquel ils appartiennent. On affichera les noms complets et les longueurs.
Rechercher les titres des disques dont le prix est plus grand que celui de Morrison Hotel.
Rechercher le titre et l'année des disques qui sont du même artiste que Flowers.
Afficher les titres et années des disques d'artistes différents qui ont le même titre. On affichera aussi le nom complet (prénom+nom+groupe) des artistes.
Le résultat d'une requête peut servir dans une clause de restriction d'une autre requête, on parlera alors de sous-requête imbriquée.
La recherche suivante peut se faire de deux manières.
Dans ce qui précède, la sous-requête retourne une seule valeur, l'opérateur égalité peut être utilisé. Si ce n'est pas le cas il faut utiliser les clauses ANY ou ALL.
En fait : « IN » est équivalent à « = ANY », tandis que « NOT IN » est équivalent à « != ALL ».
Afficher le nom complet (prénom+nom+groupe) des artistes dont au moins un disque a un prix plus bas qu'au moins un disque de Jimi HENDRIX Experience.
Afficher les titres des disques plus chers que tous les disques de l'année 1981.
Dans la table AUTORISATIONS afficher les noms et prénoms des utilisateurs de niveau membre dont on trouve aussi le prénom parmi les utilisateurs de niveau anonyme.
Dans la table AUTORISATIONS afficher les noms et prénoms des utilisateurs de niveau membre dont le prénom ne se trouve pas parmi les utilisateurs de niveau anonyme.
Rechercher le nom et le prénom des personnes qui ont le même niveau d'autorisation que Sophie FONFEC.
Renommer une table et utiliser une sous-requête permet de synchroniser une sous-requête avec la requête principale.
L'opérateur EXISTS permet de retourner des lignes si la sous-requête en retourne.
Dans la table ARTISTE il y a des artistes dont l'identifiant (art_num) ne correspond à aucune ligne de DISQUE.
Ces artistes ne peuvent pas appartenir à une jointure entre les deux tables.
Si on souhaite, malgré tout, obtenir ces artistes dans le résultat d'une jointure on utilise un LEFT JOIN.
Pour bien comprendre le sens et la syntaxe de LEFT JOIN il peut être utile de revenir sur la syntaxe des requêtes. Il faut savoir que la requête
SELECT ... FROM a,b,c WHERE a.x=b.y AND b.z=c.t ...
peut s'écrire également
SELECT ... FROM a JOIN b ON a.x=b.y JOIN c ON b.z=c.t WHERE ...
Il s'agit d'ailleurs de la syntaxe originale des jointures en SQL.
Pour écrire un LEFT JOIN, la syntaxe est la même, en remplaçant bien entendu JOIN par LEFT JOIN.
Qu'est-ce que cela va changer ? On se souvient que pour une jointure normale on ne prend que les enregistrements de chaque table qu'on peut relier par la condition de jointure. Avec un LEFT JOIN on prendra en plus les enregistrements de la table écrite à gauche (car LEFT) de l'expression LEFT JOIN et qui ne sont reliés à aucun enregistrement de celle de droite.
Il existe également RIGHT JOIN qui fonctionne de manière tout à fait symétrique à droite.
Afficher TOUS les artistes (prénom+nom+groupe) dont le nom commence par un « A » avec leurs disques. Ceux qui n'ont pas de disques doivent quand même être affichés.
Afficher tous les artistes (prénom+nom+groupe) dont le nom commence par un « A » avec leur appartenance à un groupe (prénom+nom+groupe). Ceux qui n'appartiennent pas à un groupe devront être affichés aussi.
Table des matières
Vous trouverez les réponses des exercices au Chapitre 14, Réponses aux exercices sur les dates.
Pour avoir la description de la table DISQUE, vous pouvez utiliser la commande « \d disque ». Cela vous permettra de vérifier que la colonne dis_modif est bien de type date.
L'affichage d'une date peut être modifié en utilisant la fonction TO_CHAR :
TO_CHAR(arg1,arg2)
où arg1 est une colonne (ou valeur) de type DATE, et arg2 est un masque de sortie (chaîne de caractères).
Tableau 8.1. Liste des masques au format numérique
| Masque | Signification |
|---|---|
| CC | Siècle |
| YYYY | Année |
| YYY | 3 derniers chiffres de l'année |
| YY | 2 derniers chiffres de l'année |
| Y | Le dernier chiffre de l'année |
| Q | Trimestre |
| WW | Semaine dans l'année |
| W | Semaine dans le mois |
| MM | Mois |
| DDD | Jour dans l'année |
| DD | Jour dans le mois |
| D | Jour dans la semaine |
| HH ou HH12 | Heure de la journée (1-12) |
| HH24 | Heure de la journée (0-23) |
| MI | Minutes |
| SS | Secondes |
| SSSSS | Secondes après minuit (0-86399) |
| J | Jour julien |
Exemple 8.1. Utilisation de
TO_CHAR(numérique)SELECT TO_CHAR(dis_modif,'CC'), TO_CHAR(dis_modif,'W') FROM disque ; SELECT TO_CHAR(dis_modif,'DD/MM/YYYY') FROM disque ; SELECT TO_CHAR(dis_modif, 'WWème semaine, MMème mois') FROM disque ;
Tableau 8.2. Liste des masques au format caractère
| Masque | Signification |
|---|---|
| MONTH | Nom du mois en toutes lettres |
| MON | 3 premières lettres du nom du mois |
| DAY | Nom du jour en toutes lettres |
| DY | 3 premières lettres du nom du jour |
| AM ou PM | Avant midi ou après midi |
| BC ou AD | Avant ou après J.C. |
Exemple 8.2. Utilisation de
TO_CHAR(caractères)SELECT TO_CHAR(dis_modif,'DAY MONTH YYYY') FROM disque ; SELECT TO_CHAR(dis_modif,'DY MON YYYY BC') FROM disque ;
Exemple 8.3. Utilisation de
TO_CHAR(suffixe)SELECT TO_CHAR(dis_modif, 'WWTH semaine, MMTH mois, CCTH siècle') FROM disque ;
Le masque d'affichage par défaut est
YYYY-MM-DD.
Rechercher le titre et la date de dernière modification (masque dd/mm/yy) des disques de l'année 1981.
Rechercher le titre et la date de dernière modification (masque Day DD Month YYYY) des disques de l'année 1981.
Rechercher le titre et la date de dernière modification (masque DD MON yyyy HH24:MI:SS) des disques de l'année 1981.
Afficher la date d'aujourd'hui sous la forme JJ/MM/AAAA (de deux façons, en utilisant NOW() et CURRENT_DATE).
Table des matières
Vous trouverez les réponses des exercices au Chapitre 15, Réponses aux exercices sur les groupes.
Par exemple : AVG (moyenne), MIN (minimum), MAX (maximum), SUM (somme), COUNT (dénombrement)...
Ces fonctions travaillent au niveau des groupes de lignes et non plus au niveau des lignes.
Exemple 9.1. Moyenne
Par exemple pour rechercher la moyenne des prix des disques de 1981 :
SELECT AVG(dis_prixachat) FROM disque WHERE dis_annee = '1981' ;
Avec SELECT on ne peut pas travailler à la fois au niveau des lignes et des groupes.
Si vous recherchez le titre et la moyenne des prix des disques (cette phrase a-t-elle d'ailleurs un sens ?), vous allez essayer :
SELECT dis_titre, AVG(dis_prixachat) FROM disque ;
Ce qui ne produira qu'un message d'erreur.
Par contre avec deux SELECT imbriqués on peut rechercher le titre et le prix des disques dont le prix est le plus grand.
SELECT dis_titre,dis_prixachat
FROM disque
WHERE dis_prixachat = (SELECT MAX (dis_prixachat)
FROM disque
) ;
Pour exprimer le groupe sur lequel doit porter la fonction de groupe on utilise la clause GROUP BY.
Ces fonctions et clauses peuvent s'utiliser avec une jointure.
Toute colonne qui intervient dans l'affichage sans être utilisée dans une fonction de groupe doit être aussi incluse dans la clause GROUP BY.
Pour rechercher la moyenne des prix des disques de chaque année on écrira :
SELECT dis_annee, AVG(dis_prixachat)
FROM disque
GROUP BY dis_annee ;
La clause WHERE permet d'écrire une restriction au niveau ligne, la clause HAVING permet d'écrire une restriction au niveau groupe.
Pour rechercher les années et le nombre de disques par année, pour les années représentées par plus de 20 disques, on écrira :
SELECT dis_annee,COUNT(*)
FROM disque
GROUP BY dis_annee
HAVING COUNT(*)>20
ORDER BY COUNT(*) DESC ;
Rechercher le prix maximum et le prix minimum parmi tous les disques et l'écart entre les deux.
Rechercher le nombre de chansons par genre musical et langue.
Rechercher les années et la moyenne des prix de disques par année, pour les années dont cette moyenne est supérieure à la moyenne des prix des disques de 1981.
Rechercher le nombre de cha_texte renseignées, le nombre de champs cha_libre, le nombre de champs cha_modif ansi que le nombre de lignes dans la table le nombre de champs chanson.
Vous trouverez les réponses des exercices au Chapitre 16, Réponses aux exercices récapitulatifs.
Afficher dans l'ordre alphabétique la liste des artistes qui sont référencés pour plus d'une langue.
Afficher les titres des disques qui ont le prix minimum et le prix maximum.
Afficher les disques dont le prix est le plus proche du prix moyen des disques.
Les appartenances de musiciens à des groupes, par ordre alphabétique des groupes, des membres et par ordre chronologique.
Liste des utilisateurs, dans l'ordre alphabétique des noms, prénoms, ainsi que leurs logins et niveaux d'autorisation.
Afficher en français le jour d'aujourd'hui (lundi, mardi, etc.).
La liste des disques de Pink Floyd, dans l'ordre chronologique inverse (les plus récents en premier).
Titre des chansons qui contiennent le mot « silver » dans le texte ou dans le titre.
Nombre de chansons qui contiennent le mot « love » dans le titre, et nombre de chansons qui contiennent le mot « hate » dans le titre.
Les 5 jours où il y a eu le plus de modifications de chansons, et combien.
Les disques (titre et artiste) qui comportent moins de 5 chansons.
Afficher le titre des chansons dont la dernière modification a été faite au mois de mai.
La discographie complète de Lou Reed, que ce soit seul ou dans un groupe (on précisera).
En ordre alphabétique, les musiciens qui font partie d'un groupe qui a chanté des chansons de rock.
Les artistes qui ont chanté dans plusieurs langues, et dans combien de langues.
Vous trouverez les énoncés correspondant au Chapitre 3, Une base jouet pour découvrir SQL.
Solution de l'exercice 1.
| a | b | c |
|---|---|---|
| x | m | 2 |
| x | n | 1 |
| y | m | 4 |
| z | p | 1 |
Solution de l'exercice 2.
| a |
|---|
| x |
| x |
| y |
| z |
Solution de l'exercice 3.
| a |
|---|
| x |
| z |
Solution de l'exercice 4.
| a |
|---|
| x |
| x |
| z |
Solution de l'exercice 5.
| a |
|---|
| x |
| z |
Solution de l'exercice 6.
| a |
|---|
| x |
| y |
| x |
| z |
Solution de l'exercice 7.
| a | e |
|---|---|
| x | 8 |
| x | 4 |
| x | 1 |
| x | 8 |
| x | 4 |
| x | 1 |
| y | 8 |
| y | 4 |
| y | 1 |
| z | 8 |
| z | 4 |
| z | 1 |
On remarquera que cela donne 12 lignes c'est-à-dire 4×3, soit le produit du nombre de lignes de UN et du nombre de lignes de DEUX.
Solution de l'exercice 8.
| a | e |
|---|---|
| x | 1 |
| y | 4 |
| z | 1 |
Vous trouverez les énoncés correspondant au Chapitre 6, La commande
SELECT, syntaxe de base.
Solution de l'exercice 1.
SELECT * FROM genre ;
Solution de l'exercice 2.
SELECT * FROM langue ;
Solution de l'exercice 3.
SELECT aut_nom, aut_prenom, aut_login FROM autorisations ;
Solution de l'exercice 4.
SELECT aut_prenom FROM autoristations ;
Solution de l'exercice 5.
SELECT DISTINCT aut_prenom FROM autoristations ;
Solution de l'exercice 6.
SELECT dis_titre FROM disque WHERE dis_annee='1969' AND dis_etat=2 ;
La valeur 1969 est entre deux « ' » car la colonne DIS_ANNEE est alpha-numérique, ce qui n'est pas le cas pour DIS_ETAT qui est numérique.
Solution de l'exercice 7.
SELECT gen_num, gen_nom FROM genre WHERE gen_num>5 ;
Solution de l'exercice 8.
SELECT dis_titre, dis_prixachat, dis_cddb FROM disque WHERE dis_annee = '1981' AND dis_etat = 2 AND dis_prixachat > 15 ;
Solution de l'exercice 9.
SELECT dis_titre,dis_cddb,dis_annee
FROM disque
WHERE dis_titre='IV'
OR dis_titre='Killer'
OR dis_titre='No Control' ;
Solution de l'exercice 10.
SELECT dis_titre,dis_cddb,dis_annee FROM disque WHERE dis_annee='1971' AND (dis_titre='IV' OR dis_titre='Killer') ;
Si les clauses de restriction sont reliées par « AND » ou « OR », ATTENTION aux parenthèses.
Solution de l'exercice 11.
SELECT dis_titre,dis_cddb,dis_annee
FROM disque
WHERE dis_titre='IV'
OR (dis_annee='1971' AND dis_titre='Killer') ;
Solution de l'exercice 12.
SELECT dis_titre,dis_cddb,dis_prixachat FROM disque WHERE dis_prixachat >= 13.5 AND dis_prix <= 14 ;
La clause « WHERE dis_prixachat >= 13.5 AND dis_prix <= 14 » peut s'écrire avec l'opérateur « BETWEEN » :
SELECT dis_titre,dis_cddb,dis_prixachat FROM disque WHERE dis_prixachat BETWEEN 13.5 AND 14 ;
Solution de l'exercice 13.
SELECT art_nom, art_prenom FROM artiste WHERE art_prenom='Louis' OR art_prenom='Jean' OR art_prenom='Alain' ;
La clause « WHERE colonne IN ('val1', 'val2') » permet d'éviter « WHERE colonne = 'val1' OR colonne = 'val2' » :
SELECT art_nom, art_prenom
FROM artiste
WHERE art_prenom IN ('Louis' , 'Jean' , 'Alain') ;
Solution de l'exercice 14.
SELECT art_nom, art_prenom FROM artiste WHERE art_nom < art_prenom ;
Solution de l'exercice 15.
SELECT art_nom FROM artiste WHERE art_nom LIKE 'H%' ;
Solution de l'exercice 16.
SELECT art_nom FROM artiste WHERE art_nom LIKE '%n' ;
Solution de l'exercice 17.
SELECT art_nom FROM artiste WHERE art_nom LIKE '__u%' ;
Solution de l'exercice 18.
SELECT dis_titre,dis_prixachat
FROM disque
WHERE dis_annee='1985'
ORDER BY dis_prixachat ;
Solution de l'exercice 19.
SELECT dis_titre,dis_prixachat
FROM disque
WHERE dis_annee='1985'
ORDER BY dis_prixachat DESC ;
Solution de l'exercice 20.
SELECT dis_titre,dis_annee
FROM disque
ORDER BY dis_annee DESC, dis_titre ;
Solution de l'exercice 21.
SELECT *
FROM artiste
WHERE art_nom LIKE 'A%'
ORDER BY art_modif ;
Solution de l'exercice 22.
SELECT * FROM artiste WHERE art_nom LIKE 'A%' AND art_modif IS NULL ;
Solution de l'exercice 23.
SELECT * FROM artiste WHERE art_nom LIKE 'A%' AND art_modif IS NOT NULL ;
Solution de l'exercice 24.
SELECT * FROM artiste WHERE art_nom LIKE 'A%' AND art_modif > '2009-01-01' ;
Par défaut, les colonnes alphanumériques non renseignées ne sont pas considérées comme une suite d'espaces, les colonnes numériques ne sont pas considérées comme égale à 0.
Solution de l'exercice 25.
SELECT * FROM artiste WHERE art_nom LIKE 'A%' AND art_modif < '2009-01-01' ;
Une fois de plus, remarquez le comportement des lignes non renseignées.
Solution de l'exercice 26.
SELECT dis_titre,dis_prixachat AS "Prix en euros",
dis_prixachat/6.5 AS "Prix en francs"
FROM disque
WHERE dis_anneeachat < '2002' ;
Solution de l'exercice 27.
SELECT int_artiste,int_chanson,int_disque,int_numero,
COALESCE(int_remarque,'Aucune remarque disponible') AS "Remarque?"
FROM interprete
WHERE int_artiste = 170 ;
Solution de l'exercice 28.
SELECT dis_titre,dis_prixachat AS "Prix en euros",
ROUND(dis_prixachat/6.5,2) AS "Prix en francs"
FROM disque
WHERE dis_anneeachat < '2002' ;
Solution de l'exercice 29.
SELECT art_prenom ||' '|| art_nom ||' '|| art_groupe AS "Artiste" FROM artiste ;
Solution de l'exercice 30.
SELECT cha_titre AS "Titre",
CASE WHEN cha_texte IS NULL
THEN 'paroles inconnues'
ELSE 'paroles disponibles'
END AS "Paroles ?"
FROM chanson
WHERE cha_titre LIKE 'A%'
ORDER BY 1 ;
Solution de l'exercice 31.
SELECT SUBSTR (art_nom, 1,5) FROM artiste ;
Solution de l'exercice 32. Pour la position à partir du début :
SELECT art_nom, STRPOS (art_nom,'r') FROM artiste ;
Pour avoir le résultat à partir de la troisième lettre, il suffit de « supprimer » les deux premières...
SELECT art_nom, STRPOS (SUBSTR(art_nom,3),'r') FROM artiste ;
Solution de l'exercice 33.
SELECT art_nom, art_prenom, UPPER(art_prenom) , LOWER(art_prenom)
FROM artiste
WHERE UPPER(art_nom) = UPPER('Russell') ;
Solution de l'exercice 34.
SELECT nom, LENGTH(nom) FROM emp ;
Solution de l'exercice 35.
SELECT TRIM(art_prenom ||' '|| UPPER(art_nom) ||' '|| art_groupe) AS "Artiste" FROM artiste ;
Solution de l'exercice 36.
SELECT aut_nom
FROM autorisations
UNION
SELECT art_nom
FROM artiste ;
Solution de l'exercice 37.
SELECT ecr_artiste AS "Artiste"
FROM ecrit
INTERSECT
SELECT int_artiste AS "Artiste"
FROM interprete ;
Solution de l'exercice 38.
SELECT lan_num
FROM langue
EXCEPT
SELECT lac_langue
FROM languechanson ;
Vous trouverez les énoncés correspondant au Chapitre 7, Les jointures et les sous-requêtes.
Solution de l'exercice 1.
SELECT aut_nom, aut_prenom, aut_login, dro_nom FROM autorisations, droits WHERE aut_droits = dro_num ;
Solution de l'exercice 2.
SELECT dis_titre, art_prenom, art_nom, art_groupe
FROM artiste, disque
WHERE dis_artiste = art_num
ORDER BY art_nom, art_prenom, art_groupe ;
Solution de l'exercice 3.
SELECT dis_titre, eta_nom FROM disque, etat WHERE dis_annee='1965' ;
Solution de l'exercice 4.
SELECT dis_titre, eta_nom FROM disque, etat WHERE dis_etat=eta_num AND dis_annee='1965' ;
Solution de l'exercice 5.
SELECT TRIM(a.art_prenom||' '||a.art_nom||' '||a.art_groupe) AS artiste,
TRIM(g.art_prenom||' '||g.art_nom||' '||g.art_groupe) AS groupe,
mem_debut AS depuis,
mem_fin AS "jusqu'à"
FROM artiste a, artiste g, membre
WHERE a.art_num = mem_membre
AND g.art_num = mem_groupe
ORDER BY g.art_nom,g.art_prenom,a.art_nom,a.art_prenom,a.art_groupe,g.art_groupe,mem_debut ;
Solution de l'exercice 6.
SELECT a.dis_titre,b.dis_titre FROM disque a,disque b WHERE a.dis_artiste = b.dis_artiste AND a.dis_num <> b.dis_num ;
Solution de l'exercice 7.
SELECT a.dis_titre,b.dis_titre FROM disque a,disque b WHERE a.dis_artiste = b.dis_artiste AND a.dis_num <> b.dis_num AND a.dis_num < b.dis_num ;
Solution de l'exercice 8.
SELECT TRIM(a.art_prenom||' '||a.art_nom||' '||a.art_groupe) AS artiste,
LENGTH(TRIM(a.art_prenom||' '||a.art_nom||' '||a.art_groupe)) AS "longueur artiste",
TRIM(g.art_prenom||' '||g.art_nom||' '||g.art_groupe) AS groupe,
LENGTH(TRIM(g.art_prenom||' '||g.art_nom||' '||g.art_groupe)) AS "longueur groupe"
FROM artiste a, artiste g, membre
WHERE a.art_num = mem_membre
AND g.art_num = mem_groupe
AND LENGTH(TRIM(a.art_prenom||' '||a.art_nom||' '||a.art_groupe))>LENGTH(TRIM(g.art_prenom||' '||g.art_nom||' '||g.art_groupe)) ;
Solution de l'exercice 9.
SELECT d.dis_titre,d.dis_prixachat FROM disque d,disque m WHERE d.dis_prixachat > m.dis_prixachat AND LOWER(m.dis_titre) = 'morrison hotel' ;
Solution de l'exercice 10.
SELECT d.dis_titre,d.dis_annee FROM disque d, disque f WHERE d.dis_artiste=f.dis_artiste AND LOWER(f.dis_titre) = 'flowers' ;
Solution de l'exercice 11.
SELECT da.dis_titre,
TRIM(aa.art_prenom||' '||aa.art_nom||' '||aa.art_groupe),da.dis_annee,
TRIM(ab.art_prenom||' '||ab.art_nom||' '||ab.art_groupe),db.dis_annee
FROM disque da,disque db,artiste aa,artiste ab
WHERE da.dis_titre = db.dis_titre
AND da.dis_artiste = aa.art_num
AND db.dis_artiste = ab.art_num
AND aa.art_num < ab.art_num ;
Solution de l'exercice 12.
SELECT d.dis_titre,d.dis_annee FROM disque d, disque f WHERE d.dis_annee=f.dis_annee AND LOWER(f.dis_titre) = 'flowers' ;
Mais aussi... (et c'est mieux !) :
SELECT dis_titre,dis_annee
FROM disque
WHERE dis_annee = (SELECT dis_annee
FROM disque
WHERE LOWER(dis_titre) = 'flowers') ;
Solution de l'exercice 13.
SELECT DISTINCT TRIM(art_prenom||' '||art_nom||' '||art_groupe)
FROM disque,artiste
WHERE dis_artiste = art_num
AND dis_prixachat < ANY (SELECT dis_prixachat
FROM disque,artiste
WHERE dis_artiste = art_num
AND LOWER(art_prenom)='jimi'
AND LOWER(art_nom)='hendrix'
AND LOWER(art_groupe)='experience') ;
Solution de l'exercice 14.
SELECT dis_titre
FROM disque
WHERE dis_prixachat > ALL (SELECT dis_prixachat
FROM disque
WHERE dis_annee = '1981') ;
Solution de l'exercice 15.
SELECT aut_prenom,aut_nom
FROM autorisations,droits
WHERE aut_droits = dro_num
AND LOWER(dro_nom) = 'membre'
AND LOWER(aut_prenom) IN (SELECT LOWER(aut_prenom)
FROM autorisations,droits
WHERE aut_droits = dro_num
AND LOWER(dro_nom) = 'anonyme') ;
Solution de l'exercice 16.
SELECT aut_prenom,aut_nom
FROM autorisations,droits
WHERE aut_droits = dro_num
AND LOWER(dro_nom) = 'membre'
AND LOWER(aut_prenom) NOT IN (SELECT LOWER(aut_prenom)
FROM autorisations,droits
WHERE aut_droits = dro_num
AND LOWER(dro_nom) = 'anonyme') ;
Solution de l'exercice 17.
SELECT aut_prenom, aut_nom
FROM autorisations
WHERE aut_droits = (SELECT aut_droits
FROM autorisations
WHERE LOWER(aut_nom) = 'fonfec'
AND LOWER(aut_prenom) = 'sophie') ;
Solution de l'exercice 18.
SELECT dis_titre,dis_prixachat,dis_artiste
FROM disque d
WHERE dis_prixachat > (SELECT AVG(dis_prixachat)
FROM disque
WHERE dis_artiste = d.dis_artiste)
ORDER BY dis_artiste ;
Solution de l'exercice 19.
SELECT TRIM(a.art_prenom||' '||a.art_nom||' '||a.art_groupe)
FROM artiste a
WHERE EXISTS (SELECT NULL
FROM disque
WHERE dis_perdu
AND dis_artiste = a.art_num) ;
Solution de l'exercice 20.
SELECT TRIM(art_prenom||' '||art_nom||' '||art_groupe)
FROM artiste
WHERE art_num NOT IN (SELECT dis_artiste
FROM disque) ;
Solution de l'exercice 21.
SELECT TRIM(art_prenom||' '||art_nom||' '||art_groupe),dis_titre
FROM artiste
LEFT JOIN disque ON dis_artiste = art_num
WHERE art_nom LIKE 'A%' ;
Solution de l'exercice 22.
Avec LEFT JOIN cela donne :
SELECT TRIM(a.art_prenom||' '||a.art_nom||' '||a.art_groupe) AS artiste,
TRIM(g.art_prenom||' '||g.art_nom||' '||g.art_groupe) AS artiste
FROM artiste a
LEFT JOIN membre ON a.art_num = mem_membre
LEFT JOIN artiste g ON g.art_num = mem_groupe
WHERE a.art_nom LIKE 'A%'
ORDER BY a.art_nom,a.art_prenom,a.art_groupe ;
On peut aussi se passer de LEFT JOIN (certains systèmes ne l'autorisent pas), mais c'est moins simple. Avec une union cela donne :
(
SELECT TRIM(a.art_prenom||' '||a.art_nom||' '||a.art_groupe) AS artiste,
TRIM(g.art_prenom||' '||g.art_nom||' '||g.art_groupe) AS artiste
FROM artiste a,membre,artiste g
WHERE a.art_num = mem_membre
AND g.art_num = mem_groupe
AND a.art_nom LIKE 'A%'
UNION
SELECT TRIM(art_prenom||' '||art_nom||' '||art_groupe) AS artiste,
NULL AS artiste
FROM artiste
WHERE art_nom LIKE 'A%'
)
ORDER BY 1 ;Mais on a une ligne vide pour chaque artiste, même quand ce n'est pas nécessaire. On peut améliorer ainsi :
(
SELECT TRIM(a.art_prenom||' '||a.art_nom||' '||a.art_groupe) AS artiste,
TRIM(g.art_prenom||' '||g.art_nom||' '||g.art_groupe) AS artiste
FROM artiste a,membre,artiste g
WHERE a.art_num = mem_membre
AND g.art_num = mem_groupe
AND a.art_nom LIKE 'A%'
UNION
SELECT TRIM(art_prenom||' '||art_nom||' '||art_groupe) AS artiste,
NULL AS artiste
FROM artiste
WHERE art_nom LIKE 'A%'
AND art_num NOT IN (SELECT mem_membre
FROM membre)
)
ORDER BY 1 ;
Vous trouverez les énoncés correspondant au Chapitre 8, Les dates.
Solution de l'exercice 1.
SELECT dis_titre,dis_modif FROM disque ; SET DATESTYLE TO 'European' ; SELECT dis_titre,dis_modif FROM disque ; SET DATESTYLE TO 'ISO' ; SELECT dis_titre,dis_modif FROM disque ; SET DATESTYLE TO 'Postgres' ; SELECT dis_titre,dis_modif FROM disque ; SET DATESTYLE TO DEFAULT ; SELECT dis_titre,dis_modif FROM disque ; SET DATESTYLE TO 'German' ; SELECT dis_titre,dis_modif FROM disque ; SET DATESTYLE TO 'NonEuropean' ; SELECT dis_titre,dis_modif FROM disque ; SET DATESTYLE TO 'SQL' ; SELECT dis_titre,dis_modif FROM disque ;
Solution de l'exercice 2.
SELECT dis_titre,TO_CHAR(dis_modif,'dd/mm/yy') FROM disque WHERE dis_annee = '1981' ;
Solution de l'exercice 3.
SELECT dis_titre,TO_CHAR(dis_modif,'Day DD Month YYYY') FROM disque WHERE dis_annee = '1981' ;
Solution de l'exercice 4.
SELECT dis_titre,TO_CHAR(dis_modif,'DD MON yyyy HH24:MI:SS') FROM disque WHERE dis_annee = '1981' ;
Solution de l'exercice 5.
SELECT TO_CHAR(NOW(),'DD/MM/YYYY') AS "Aujourd'hui",
TO_CHAR(CURRENT_DATE,'DD/MM/YYYY') AS "Aujourd'hui" ;
Solution de l'exercice 6.
SELECT TO_CHAR(NOW()::DATE - 1,'DD/MM/YYYY') AS "Hier",
TO_CHAR(CURRENT_DATE - 1,'DD/MM/YYYY') AS "Hier" ;
Solution de l'exercice 7.
SELECT CURRENT_DATE - TO_CHAR(CURRENT_DATE,'D')::INTEGER + 1 AS "Dimanche dernier" ;
Solution de l'exercice 8.
SELECT cha_titre AS titre,TO_CHAR(cha_modif,'DD/MM/YYYY') AS date,
(CURRENT_DATE-cha_modif::date)||' jours' AS Delai
FROM chanson
WHERE cha_modif=(SELECT MAX(cha_modif) FROM chanson) ;
Vous trouverez les énoncés correspondant au Chapitre 9, Les groupes.
Solution de l'exercice 1.
SELECT dis_annee,COUNT(*)
FROM disque
GROUP BY dis_annee ;
Solution de l'exercice 2.
SELECT TRIM(art_prenom||' '||art_nom||' '||art_groupe) AS artiste,
AVG(dis_prixachat) as moyenne, SUM(dis_prixachat) as somme
FROM disque,artiste
WHERE dis_artiste = art_num
GROUP BY art_num,art_prenom,art_nom,art_groupe
ORDER BY art_nom,art_prenom,art_groupe ;
Solution de l'exercice 3.
SELECT TRIM(art_prenom||' '||art_nom||' '||art_groupe) AS artiste,COUNT(*) AS nbre
FROM artiste,disque
WHERE art_num = dis_artiste
GROUP BY art_num,art_prenom,art_nom,art_groupe
HAVING COUNT(*)>=10
ORDER BY COUNT(*) DESC ;
Solution de l'exercice 4.
SELECT SUBSTR(art_nom,1,1), COUNT(*)
FROM artiste
GROUP BY SUBSTR(art_nom,1,1)
HAVING COUNT(*) >= 20
ORDER BY 1 ;
Solution de l'exercice 5.
SELECT MAX(dis_prixachat) ,
MIN(dis_prixachat) ,
MAX(dis_prixachat) - MIN(dis_prixachat) AS "Différence"
FROM disque ;
Solution de l'exercice 6.
SELECT COUNT(DISTINCT dis_annee) FROM disque ;
Essayez aussi, pour voir la différence
SELECT COUNT(dis_annee) FROM disque ;
Solution de l'exercice 7.
SELECT gen_nom,lan_nom,COUNT(*)
FROM chanson,genre,langue,languechanson
WHERE gen_num=cha_genre
AND cha_num=lac_chanson
AND lac_langue=lan_num
GROUP BY lan_nom,gen_nom,lan_num,gen_num
ORDER BY gen_nom,lan_nom ;
Solution de l'exercice 8.
SELECT dis_annee, AVG(dis_prixachat)
FROM disque
GROUP BY dis_annee
HAVING AVG(dis_prixachat) >= (SELECT AVG(dis_prixachat)
FROM disque
WHERE dis_annee = '1981')
ORDER BY 2 DESC ;
Solution de l'exercice 9.
SELECT COUNT(cha_texte) AS texte,
COUNT(cha_libre) AS libre,
COUNT(cha_modif) AS modif,
COUNT(*) AS total
FROM chanson ;
Vous trouverez les énoncés correspondant au Chapitre 10, Exercices récapitulatifs.
Solution de l'exercice 1.
SELECT COUNT(DISTINCT laa_langue) AS "nbre de langues",
TRIM(art_prenom||' '||art_nom||' '||art_groupe) AS artiste
FROM artiste,langueartiste
WHERE art_num=laa_artiste
GROUP BY art_nom,art_prenom,art_groupe,art_num
HAVING COUNT(DISTINCT laa_langue) > 1 ;
Solution de l'exercice 2.
(
SELECT dis_titre,dis_prixachat,'Minimum' AS "min ou max?"
FROM disque
WHERE dis_prixachat=(
SELECT MIN(dis_prixachat)
FROM disque
)
)
UNION
(
SELECT dis_titre,dis_prixachat,'Maximum' AS "min ou max?"
FROM disque
WHERE dis_prixachat=(
SELECT MAX(dis_prixachat)
FROM disque
)
)
ORDER BY 2 ;
Solution de l'exercice 3.
SELECT TRIM(art_prenom||' '||art_nom||' '||art_groupe) AS artiste,COUNT(*) AS nombre
FROM artiste,disque
WHERE art_num = dis_artiste
GROUP BY art_prenom,art_nom,art_groupe,art_num
HAVING COUNT(*) = (
SELECT MAX(total)
FROM (
SELECT dis_artiste,COUNT(*) AS total
FROM disque
GROUP BY dis_artiste
) nbreParArtiste
) ;
Solution de l'exercice 4.
SELECT dis_titre,dis_prixachat
FROM disque
WHERE (
SELECT ABS(dis_prixachat-(SELECT AVG(dis_prixachat)
FROM disque))
)=(
SELECT MIN(diff)
FROM (
SELECT ABS(dis_prixachat-(
SELECT AVG(dis_prixachat)
FROM disque
)
) AS diff
FROM disque
) a
) ;ou
SELECT dis_titre,dis_prixachat
FROM disque,(
SELECT AVG(dis_prixachat) AS moy
FROM disque
) r1,
(
SELECT MIN(diff) AS mini
FROM (
SELECT ABS(dis_prixachat-moy) AS diff
FROM disque,(
SELECT AVG(dis_prixachat) AS moy
FROM disque
) r2
) r3
) r4
WHERE mini = ABS(dis_prixachat-moy) ;
Solution de l'exercice 5.
SELECT eta_nom,COUNT(*)
FROM disque,etat
WHERE dis_etat=eta_num
GROUP BY eta_nom ;
Solution de l'exercice 6.
SELECT gen_nom,COUNT(*)
FROM chanson,genre
WHERE gen_num=cha_genre
GROUP BY gen_nom,gen_num ;
Solution de l'exercice 7.
SELECT lan_nom,COUNT(*)
FROM chanson,languechanson,langue
WHERE cha_num=lac_chanson
AND lan_num=lac_langue
GROUP BY lan_nom ;
Solution de l'exercice 8.
SELECT COUNT(*) FROM chanson WHERE cha_libre ;
Solution de l'exercice 9.
SELECT TRIM(m.art_prenom||' '||m.art_nom||' '||m.art_groupe) AS artiste,
TRIM(g.art_prenom||' '||g.art_nom||' '||g.art_groupe) AS groupe,
CASE WHEN mem_debut<>'' THEN mem_debut ELSE '' END AS depuis,
CASE WHEN mem_fin<>'' THEN mem_fin ELSE '' END AS jusque
FROM artiste m,membre,artiste g
WHERE m.art_num=mem_membre
AND mem_groupe=g.art_num
ORDER BY g.art_nom,g.art_prenom,g.art_groupe,
m.art_nom,m.art_prenom,m.art_groupe,
mem_debut,mem_fin ;
Solution de l'exercice 10.
SELECT aut_nom, aut_prenom, aut_login, dro_nom
FROM autorisations, droits
WHERE aut_droits = dro_num
ORDER BY 1,2 ;
Solution de l'exercice 11.
SELECT CASE
WHEN TO_CHAR(CURRENT_DATE,'D')='1' THEN 'dimanche'
WHEN TO_CHAR(CURRENT_DATE,'D')='2' THEN 'lundi'
WHEN TO_CHAR(CURRENT_DATE,'D')='3' THEN 'mardi'
WHEN TO_CHAR(CURRENT_DATE,'D')='4' THEN 'mercredi'
WHEN TO_CHAR(CURRENT_DATE,'D')='5' THEN 'jeudi'
WHEN TO_CHAR(CURRENT_DATE,'D')='6' THEN 'vendredi'
ELSE 'samedi'
END AS "jour d'aujourd'hui" ;
Solution de l'exercice 12.
SELECT TO_CHAR(NOW(),'HH24:MI:SS') AS "Au troisième top..." ;
Solution de l'exercice 13.
SELECT COUNT(*) AS nbre FROM artiste,interprete WHERE art_num=int_artiste AND art_prenom='Robert' AND art_nom='Cray' AND art_groupe='' ;
Solution de l'exercice 14.
SELECT dro_nom AS Nom,dro_niveau AS niveau,COUNT(*) AS nbre
FROM autorisations,droits
WHERE aut_droits=dro_num
GROUP BY dro_nom,dro_niveau ;
Solution de l'exercice 15.
SELECT dis_titre,dis_annee
FROM disque,artiste
WHERE dis_artiste=art_num
AND art_nom='Pink Floyd'
ORDER BY 2 DESC,1 ;
Solution de l'exercice 16.
SELECT TRIM(art_prenom||' '||art_nom||' '||art_groupe) AS artiste,COUNT(*)
FROM disque,artiste
WHERE dis_artiste=art_num
GROUP BY art_num,art_prenom,art_nom,art_groupe
ORDER BY COUNT(*) DESC
LIMIT 10 ;
Solution de l'exercice 17.
SELECT cha_titre
FROM chanson
WHERE LOWER(cha_titre) LIKE '%silver%'
OR LOWER(cha_texte) LIKE '%silver%'
ORDER BY cha_titre ;
Solution de l'exercice 18.
SELECT 'Love' AS "Love hate", (SELECT COUNT(*)
FROM chanson
WHERE LOWER(cha_titre) LIKE '%love%') AS nombre
UNION
SELECT 'Hate' AS "Love hate", (SELECT COUNT(*)
FROM chanson
WHERE LOWER(cha_titre) LIKE '%hate%') AS nombre ;
Solution de l'exercice 19.
SELECT TO_CHAR(cha_modif,'DD/MM/YYYY') AS jour,COUNT(*) AS nombre
FROM chanson
GROUP BY TO_CHAR(cha_modif,'DD/MM/YYYY')
ORDER BY COUNT(*) DESC
LIMIT 5 ;
Solution de l'exercice 20.
SELECT TO_CHAR(cha_modif,'HH24') AS heure,COUNT(*) AS nombre
FROM chanson
GROUP BY TO_CHAR(cha_modif,'HH24')
ORDER BY 1 ;
Solution de l'exercice 21.
SELECT TRIM(art_prenom||' '||art_nom||' '||art_groupe) AS artiste,COUNT(*) AS nombre
FROM artiste,ecrit
WHERE art_num=ecr_artiste
GROUP BY art_num,art_prenom,art_nom,art_groupe
ORDER BY art_nom,art_prenom,art_groupe ;
Solution de l'exercice 22.
SELECT lan_nom,COUNT(*)
FROM langue,languechanson
WHERE lan_num=lac_langue
GROUP BY lan_nom
ORDER BY 1,2 ;
Solution de l'exercice 23.
SELECT dis_titre AS disque,
TRIM(art_prenom||' '||UPPER(art_nom)||' '||art_groupe) AS artiste,
COUNT(*) AS Nbre
FROM disque,interprete,artiste
WHERE dis_num=int_disque
AND art_num=dis_artiste
GROUP BY dis_num,dis_titre,art_prenom,art_nom,art_groupe
HAVING COUNT(DISTINCT int_numero)<5 ;
Solution de l'exercice 24.
SELECT cha_titre FROM chanson WHERE TO_CHAR(cha_modif,'MM')::INTEGER=5 ;
Solution de l'exercice 25.
SELECT cha_num,cha_titre,COUNT(DISTINCT int_disque||'-'||int_numero)
FROM chanson,interprete
WHERE cha_num=int_chanson
GROUP BY cha_num,cha_titre
HAVING COUNT(DISTINCT int_disque||'-'||int_numero)>=5
ORDER BY COUNT(DISTINCT int_disque||'-'||int_numero) DESC ;
Solution de l'exercice 26.
SELECT int_numero,cha_titre
FROM chanson,interprete,disque
WHERE cha_num=int_chanson
AND int_disque=dis_num
AND LOWER(dis_titre)='déjà vu'
ORDER BY int_numero ;
Solution de l'exercice 27.
(
SELECT dis_titre AS "Discographie",dis_annee AS "Année",
'Seul' AS "Conditions"
FROM artiste,disque
WHERE art_num=dis_artiste
AND art_nom='Reed'
AND art_prenom='Lou'
AND art_groupe=''
)
UNION
(
SELECT dis_titre AS "Discographie",dis_annee AS "Année",
'avec '||TRIM(g.art_prenom||' '||UPPER(g.art_nom)||' '||g.art_groupe) AS "Conditions"
FROM artiste a,artiste g,membre,disque
WHERE g.art_num=dis_artiste
AND a.art_nom='Reed'
AND a.art_prenom='Lou'
AND a.art_groupe=''
AND g.art_num=mem_groupe
AND a.art_num=mem_membre
)
ORDER BY 2 ;
Solution de l'exercice 28.
SELECT DISTINCT art_prenom,art_nom,art_groupe
FROM artiste,membre,interprete,chanson,genre
WHERE art_num=mem_membre
AND mem_groupe=int_artiste
AND int_chanson=cha_num
AND cha_genre=gen_num
AND LOWER(gen_nom)='rock'
ORDER BY art_nom,art_prenom,art_groupe ;
Solution de l'exercice 29.
SELECT TRIM(art_prenom||' '||UPPER(art_nom)||' '||art_groupe) AS artiste,
count(distinct lan_nom) AS "nbre de langues"
FROM artiste,interprete,languechanson,langue
WHERE art_num=int_artiste
AND int_chanson=lac_chanson
AND lac_langue=lan_num
GROUP BY art_prenom,art_nom,art_groupe
HAVING COUNT(DISTINCT lan_num)>1
ORDER BY 1,2 ;