Base pour les exemples
Pour expliquer les notions en jeu, nous allons utiliser la base présentée ci-dessous par ses trois tables.
La table COMPANY
| id | nom | age | adresse | salaire | service |
| 1 | Paul | 32 | California | 20000 | 3 |
| 2 | Allen | 25 | Texas | 15000 | 3 |
| 3 | Teddy | 23 | Norway | 20000 | 2 |
| 4 | Mark | 25 | Rich-Mond | 65000 | 1 |
| 5 | David | 27 | Texas | 85000 | 1 |
| 6 | Kim | 22 | South-Hall | 45000 | 2 |
| 7 | James | 24 | Houston | 10000 | 3 |
Par exemple, Paul habite en Californie, gagne un salaire de 20 000 par an et travaille dans le service d'identifiant 3.
Elle est définie par le code :
CREATE TABLE "COMPANY" (
`id` INTEGER NOT NULL,
`nom` TEXT NOT NULL,
`age` INTEGER,
`adresse` TEXT NOT NULL,
`salaire` INTEGER NOT NULL,
`service` INTEGER,
PRIMARY KEY(id),
FOREIGN KEY(`service`) REFERENCES SERVICE
)
La table SERVICE
Elle est définie par le code :
CREATE TABLE "SERVICE" (
`id_service` INTEGER NOT NULL,
`denomination` TEXT NOT NULL,
`chef_de_service` INTEGER NOT NULL,
`adresse` TEXT,
PRIMARY KEY(id_service),
FOREIGN KEY(`chef_de_service`) REFERENCES COMPANY
)
chef_de_service est une clef étrangère faisant référence à l'id de la table COMPANY.
Son contenu est le suivant :
| id_service | denomination | chef_de_service | adresse |
| 1 | comptabilité | 4 | rue de Marseille |
| 2 | facturation | 6 | rue de Marseille |
| 3 | ingénierie | 7 | rue du travail |
On lit dans cette table que le chef de service du service comptabilité a l'identifiant 4, il s'agit donc de Mark du Rich-Mond.
La table DOSSIER
Son contenu :
| id_tache | NumeroDossier | id_personne |
| 1 | 4568 | 2 |
| 2 | 123 | 7 |
| 3 | 4568 | 1 |
| 4 | 123 | 1 |
| 5 | 8974 | 6 |
| 6 | 8974 | 1 |
| 7 | 4447 | 3 |
| 8 | 4447 | 1 |
Lecture : la personne d'indentifiant 2 (il s'agit donc du texan Allen) a travaillé sur le dossier 4568.
Le code de création :
CREATE TABLE `DOSSIER` (
`id_tache` INTEGER PRIMARY KEY AUTOINCREMENT,
`NumeroDossier` INTEGER NOT NULL,
`id_personne` INTEGER NOT NULL,
FOREIGN KEY(`id_personne`) REFERENCES COMPANY
)
Source
Vous pouvez télécharger ici cette base pour tester les exemples qui suivent. Prenez le temps de tester chacune des requêtes proposées.
Qu'est-ce qu'une sous-requête ?
Une sous-requête (ou requête interne) est une requête située à l'intérieur d'une autre requête.
Une sous-requête sert à "conditionner", à préciser ce qui sera retournée par la requête qui la contient.
Un premier exemple
Dans la table COMPANY, on aimerait avoir les noms et salaires des personnes qui gagnent plus que Teddy.
On écrit une première requête, qui constituera la requête interne, sélectionnant le salaire de Teddy : SELECT salaire FROM COMPANY where nom = "Teddy". Puis on englobe cette requête dans une requête sélectionnant les noms et salaires des personnes pour lesquelles le salaire est supérieur au résultat de la requête interne.
Ce qui donne :
select nom, salaire from COMPANY where salaire >
(SELECT salaire FROM COMPANY where nom = "Teddy");
In, Not in
Afficher les noms des personnes travaillant rue de Marseille (c'est à dire travaillant pour un service dont l'adresse est rue de Marseille).
SELECT nom FROM COMPANY where COMPANY.service IN
(SELECT SERVICE.id_service from SERVICE where adresse = "rue de Marseille");
et ceux qui ne travaillent pas rue de Marseille :
SELECT nom FROM COMPANY where COMPANY.service NOT IN
(SELECT SERVICE.id_service from SERVICE where adresse = "rue de Marseille");
Exists
Dans la requête ci-dessous, on utilise la table COMPANY dans la requête principale et dans la requête interne. Et le champ service de cette table apparaît aux deux niveaux. Pour savoir de quelle ligne la valeur service est tirée (ligne de la requête principale ou de la requête interne), on renomme la table COMPANY avec un pseudo : la table COMPANY au niveau principal est renommée ici C, elle est renommée CC au niveau sous-requête.
Une ligne de la table COMPANY C est affichée si son attribut "service" est tel qu'il existe au moins une ligne avec ce service où le salaire est supérieur à 40 000.
select distinct service from COMPANY as C
where exists (select * from COMPANY as CC where salaire > 40000 and CC.service = C.service);
La requête permet donc de savoir qu'il existe des salaires supérieurs à 40000 dans deux services (services d'identifiant 1 et service d'identifiant 2).
Pour connaître les services dans lesquels tous les salaires sont d'au plus 40000, on pourra donc écrire :
select distinct service from COMPANY as C where
not exists (select * from COMPANY as CC where salaire > 40000 and CC.service = C.service);
Pour connaître les services dans lesquels tous les salaires sont d'au plus 40000, on peut aussi écrire :
select distinct service from COMPANY as C where
(select max(salaire) from COMPANY as CC where CC.service = C.service) <= 40000 ;
Tous les...
A l'aide de la table DOSSIER, on aimerait obtenir les identifiants des personnes ayant travaillé sur tous les dossiers.
Première solution
On veut sélectionner les personnes ayant travaillé sur tout dossier. Il est équivalent de sélectionner les personnes pour lesquelles il n'existe pas de dossier sur lequel la personne n'a pas travaillé.
Cela peut se traduire par :
select distinct D1.id_personne from DOSSIER as D1 where
not exists
(
select D2.NumeroDossier from DOSSIER as D2
where not exists
(
select * from DOSSIER as D3
where D3.id_personne = D1.id_personne and
D3.NumeroDossier =D2.NumeroDossier
)
)
D1.id_personne et D2.NumeroDossier étant fixés, la demande la plus interne sélectionne les lignes faisant apparaître cette personne et ce dossier. La requête du niveau au-dessus sélectionne donc les dossiers pour lesquels la personne D1.id_personne n'a pas travaillé.
Et la requête la plus externe sélectionne donc les personnes pour lesquelles il n'existe pas de dossier sur lesquels elles n'ont pas travaillé.
Deuxième solution
Une personne ayant travaillé sur tous les dossiers est une personne dont le nombre de dossiers traités est égal au nombre total de dossiers existants.
select distinct id_personne from DOSSIER
group by id_personne
having count(distinct NumeroDossier) = ( select count( distinct NumeroDossier) from DOSSIER )