Previous Up Next
Accueil

4.5  Interroger une base – Langage de manipulation de données (LMD) : SELECT (1 ère partie)

4.5.1  Introduction à la commande SELECT

Introduction

La commande SELECT constitue, à elle seule, le langage permettant d’interroger une base de données. Elle permet de :

Une requête (i.e. une interrogation) est une combinaison d’opérations portant sur des tables (relations) et dont le résultat est lui-même une table dont l’existence est éphémère (le temps de la requête).

Syntaxe simplifiée de la commande SELECT

Une requête se présente généralement sous la forme :

SELECT  [ ALL | DISTINCT ] { * | attribut [, ...] } 
    FROM nom_table [, ...]
    [ WHERE condition ]

Par exemple, pour afficher l’ensemble des n-uplets de la table film, vous pouvez utiliser la requête :

SELECT * FROM film

De manière synthétique, on peut dire que la clause SELECT permet de réaliser la projection, la clause FROM le produit cartésien et la clause WHERE la sélection (cf. section 4.5.2).

Délimiteurs : apostrophes simples et doubles

Pour spécifier littéralement une chaîne de caractères, il faut l’entourer d’apostrophes (i.e. guillemets simples). Par exemple, pour sélectionner les films policiers, on utilise la requête :

SELECT * FROM film WHERE genre='Policier'

Les date doivent également être entourée d’apostrophes (ex : ’01/01/2005’).

Comme l’apostrophe est utilisée pour délimiter les chaînes de caractères, pour la représenter dans une chaîne, il faut la dédoubler (exemple : ’l’’arbre’), ou la faire précéder d’un antislash (exemple : 'l\'arbre').

Lorsque le nom d’un élément d’une base de données (un nom de table ou de colonne par exemple) est identique à un mot clef du SQL, il convient de l’entourer d’apostrophes doubles. Par exemple, si la table achat possède un attribut date, on pourra écrire :

SELECT ''date'' FROM achat

Bien entendu, les mots réservés du SQL sont déconseillés pour nommer de tels objets. Les apostrophes doubles sont également nécessaires lorsque le nom (d’une colonne ou d’une table) est composé de caractères particuliers tels que les blancs ou autres, ce qui est évidemment déconseillé.

4.5.2  Traduction des opérateurs de projection, sélection, produit cartésien et équi-jointure de l’algèbre relationnelle (1 ère partie)

Traduction de l’opérateur de projection

L’opérateur de projection Π(A1, … An)(relation) se traduit tout simplement en SQL par la requête :

SELECT DISTINCT A_1, ..., A_n FROM relation

DISTINCT permet de ne retenir qu’une occurrence de n-uplet dans le cas où une requête produit plusieurs n-uplets identiques (cf. section 4.5.4).

Traduction de l’opérateur de sélection

L’opérateur de sélection σ(prédicat)(relation) se traduit tout simplement en SQL par la requête :

SELECT * FROM relation WHERE prédicat

De manière simplifiée, un prédicat est une expression logique sur des comparaisons. Reportez-vous à la section 4.5.7 pour une description plus complète.

Traduction de l’opérateur de produit cartésien

L’opérateur de produit cartésien relation1 × relation2 se traduit en SQL par la requête :

SELECT * FROM relation_1, relation_2

Nous reviendrons sur le produit cartésien dans les sections 4.5.5 et 4.7.1.

Traduction de l’opérateur d’équi-jointure

L’opérateur d’équi-jointure relation1 ▷◁A1,A2 relation2 se traduit en SQL par la requête :

SELECT * FROM relation_1, relation_2 WHERE relation_1.A_1 = relation_2.A_2

Nous reviendrons sur les différents types de jointure dans la section 4.7.1.

4.5.3  Syntaxe générale de la commande SELECT

Voici la syntaxe générale d’une commande SELECT :

SELECT [ ALL | DISTINCT ] { * | expression [ AS nom_affiché ] } [, ...]
    FROM nom_table [ [ AS ] alias ] [, ...]
    [ WHERE prédicat ]
    [ GROUP BY expression [, ...] ]
    [ HAVING condition [, ...] ]
    [ {UNION | INTERSECT | EXCEPT [ALL]} requête ]
    [ ORDER BY expression [ ASC | DESC ] [, ...] ]

En fait l’ordre SQL SELECT est composé de 7 clauses dont 5 sont optionnelles :

SELECT :
Cette clause permet de spécifier les attributs que l’on désire voir apparaître dans le résultat de la requête (cf. section 4.5.4).
FROM :
Cette clause spécifie les tables sur lesquelles porte la requête (cf. section 4.5.5 et 4.7.1).
WHERE :
Cette clause permet de filtrer les n-uplets en imposant une condition à remplir pour qu’ils soient présents dans le résultat de la requête (cf. section 4.5.7).
GROUP BY :
Cette clause permet de définir des groupes (i.e. sous-ensemble ; cf. section 4.7.2).
HAVING :
Cette clause permet de spécifier un filtre (condition de regroupement des n-uplets) portant sur les résultats (cf. section 4.7.2).
UNION, INTERSECT et EXCEPT :
Cette clause permet d’effectuer des opérations ensemblistes entre plusieurs résultats de requête (i.e. entre plusieurs SELECT) (cf. section 4.7.3).
ORDER BY :
Cette clause permet de trier les n-uplets du résultat (cf. section 4.5.6).

4.5.4  La clause SELECT

Introduction

Comme nous l’avons déjà dit, la clause SELECT permet de spécifier les attributs que l’on désire voir apparaître dans le résultat de la requête. Pour préciser explicitement les attributs que l’on désire conserver, il faut les lister en les séparant par une virgule. Cela revient en fait à opérer une projection de la table intermédiaire générée par le reste de la requête. Nous verrons dans cette section que la clause SELECT permet d’aller plus loin que la simple opération de projection. En effet, cette clause permet également de renommer des colonnes, voire d’en créer de nouvelles à partir des colonnes existantes.

Pour illustrer par des exemples les sections qui suivent, nous utiliserons une table dont le schéma est le suivant :

employee(id_employee, surname, name, salary)

Cette table contient respectivement l’identifiant, le nom, le prénom et le salaire mensuel des employés d’une compagnie.

L’opérateur étoile (*)

Le caractère étoile (*) permet de récupérer automatiquement tous les attributs de la table générée par la clause FROM de la requête.

Pour afficher la table employee on peut utiliser la requête :

SELECT * FROM employee

Les opérateurs DISTINCT et ALL

Lorsque le SGBD construit la réponse d’une requête, il rapatrie toutes les lignes qui satisfont la requête, généralement dans l’ordre ou il les trouve, même si ces dernières sont en double (comportement ALL par défaut). C’est pourquoi il est souvent nécessaire d’utiliser le mot clef DISTINCT qui permet d’éliminer les doublons dans la réponse.

Par exemple, pour afficher la liste des prénoms, sans doublon, des employés de la compagnie, il faut utiliser la requête :

SELECT DISTINCT name FROM employee

Les opérations mathématiques de base

Il est possible d’utiliser les opérateurs mathématiques de base (i.e. +, -, * et /) pour générer de nouvelles colonnes à partir, en générale, d’une ou plusieurs colonnes existantes.

Pour afficher le nom, le prénom et le salaire annuel des employés, on peut utiliser la requête :

SELECT surname, name, salary*12 FROM employee

L’opérateur AS

Le mot clef AS permet de renommer une colonne, ou de nommer une colonne créée dans la requête.

Pour afficher le nom, le prénom et le salaire annuel des employés, on peut utiliser la requête :

SELECT surname AS nom, name AS prénom, salary*12 AS salaire FROM employee

L’opérateur de concaténation

L’opérateur || (double barre verticale) permet de concaténer des champs de type caractères.

Pour afficher le nom et le prénom sur une colonne, puis le salaire annuel des employés, on peut utiliser la requête :

SELECT surname || ' ' || name AS nom, salary*12 AS salaire FROM employee

4.5.5  La clause FROM (1 ère partie)

Comportement

Comme nous l’avons déjà dit, la clause FROM spécifie les tables sur lesquelles porte la requête. Plus exactement, cette clause construit la table intermédiaire (i.e. virtuelle), à partir d’une ou de plusieurs tables, sur laquelle des modifications seront apportées par les clauses SELECT, WHERE, GROUP BY et HAVING pour générer la table finale résultat de la requête. Quand plusieurs tables, séparées par des virgules, sont énumérées dans la clause FROM, la table intermédiaire est le résultat du produit cartésien de toutes les tables énumérées.

L’opérateur AS

Le mot clef AS permet de renommer une table, ou de nommer une table créée dans la requête (c’est à dire une sous-requête) afin de pouvoir ensuite y faire référence. Le renommage du nom d’une table se fait de l’une des deux manières suivantes :

FROM nom_de_table AS nouveau_nom
FROM nom_de_table nouveau_nom

Une application typique du renommage de table est de simplifier les noms trop long :

SELECT * FROM nom_de_table_1 AS t1, nom_de_table_1 AS t2 WHERE t1.A_1 = t2.A_2

Attention, le nouveau nom remplace complètement l’ancien nom de la table dans la requête. Ainsi, quand une table a été renommée, il n’est plus possible d’y faire référence en utilisant son ancien nom. La requête suivante n’est donc pas valide :

SELECT * FROM nom_table AS t WHERE nom_table.a > 5

Sous-requête

Les tables mentionnées dans la clause FROM peuvent très bien correspondre à des tables résultant d’une requête, spécifiée entre parenthèses, plutôt qu’à des tables existantes dans la base de données. Il faut toujours nommer les tables correspondant à des sous-requêtes en utilisant l’opérateur AS.

Par exemple, les deux requêtes suivantes sont équivalentes :

SELECT * FROM table_1, table_2
SELECT * FROM (SELECT * FROM table_1) AS t1, table_2

Les jointures

Nous traiterons cet aspect de la clause FROM dans la section 4.7.1.

4.5.6  La clause ORDER BY

Comme nous l’avons déjà dit, la clause ORDER BY permet de trier les n-uplets du résultat et sa syntaxe est la suivante :

ORDER BY expression [ ASC | DESC ] [, ...]

expression désigne soit une colonne, soit une opération mathématique de base (nous avons abordé ce type d’opérations dans la section 4.5.4 sur « La clause SELECT ») sur les colonnes.

ASC spécifie l’ordre ascendant et DESC l’ordre descendant du tri. En l’absence de précision ASC ou DESC, c’est l’ordre ascendant qui est utilisé par défaut.

Quand plusieurs expressions, ou colonnes sont mentionnées, le tri se fait d’abord selon les premières, puis suivant les suivantes pour les n-uplet qui sont égaux selon les premières.

Le tri est un tri interne sur le résultat final de la requête, il ne faut donc placer dans cette clause que les noms des colonnes mentionnés dans la clause SELECT.

La clause ORDER BY permet de trier le résultat final de la requête, elle est donc la dernière clause de tout ordre SQL et ne doit figurer qu’une seule fois dans le SELECT, même s’il existe des requêtes imbriquées ou un jeu de requêtes ensemblistes (cf. section 4.7.3).

En l’absence de clause ORDER BY, l’ordre des n-uplet est aléatoire et non garanti. Souvent, le fait de placer le mot clef DISTINCT suffit à établir un tri puisque le SGBD doit se livrer à une comparaison des lignes, mais ce mécanisme n’est pas garanti car ce tri s’effectue dans un ordre non contrôlable qui peut varier d’un serveur à l’autre.

4.5.7  La clause WHERE

Comportement

Comme nous l’avons déjà dit, la clause WHERE permet de filtrer les n-uplets en imposant une condition à remplir pour qu’ils soient présents dans le résultat de la requête ; sa syntaxe est la suivante :

WHERE prédicat

Concrètement, après que la table intermédiaire (i.e. virtuelle) de la clause FROM a été construite, chaque ligne de la table est confrontée au prédicat prédicat afin de vérifier si la ligne satisfait (i.e. le prédicat est vrai pour cette ligne) ou ne satisfait pas (i.e. le prédicat est faux ou NULL pour cette ligne) le prédicat. Les lignes qui ne satisfont pas le prédicat sont supprimées de la table intermédiaire.

Le prédicat n’est rien d’autre qu’une expression logique. En principe, celle-ci fait intervenir une ou plusieurs lignes de la table générée par la clause FROM, cela n’est pas impératif mais, dans le cas contraire, l’utilité de la clause WHERE serait nulle.

Expression simple

Une expression simple peut être une variable désignée par un nom de colonne ou une constante. Si la variable désigne un nom de colonne, la valeur de la variable sera la valeur située dans la table à l’intersection de la colonne et de la ligne dont le SGBD cherche à vérifier si elle satisfait le prédicat de la clause WHERE.

Les expressions simples peuvent être de trois types : numérique, chaîne de caractères ou date.

Une expression simple peut également être le résultat d’une sous-requête, spécifiée entre parenthèses, qui retourne une table ne contenant qu’une seule ligne et qu’une seule colonne (i.e. une sous-requête retournant une valeur unique).

Prédicat simple

Un prédicat simple peut être le résultat de la comparaison de deux expressions simples au moyen de l’un des opérateurs suivants :

=égal
!=différent
<strictement inférieur
<=inférieur ou égal
>strictement supérieur
>=supérieur ou égal

Dans ce cas, les trois types d’expressions (numérique, chaîne de caractères et date) peuvent être comparés. Pour les types date, la relation d’ordre est l’ordre chronologique. Pour les caractères, la relation d’ordre est l’ordre lexicographique.

Un prédicat simple peut également correspondre à un test de description d’une chaîne de caractères par une expression régulière :

~décrit par l’expression régulière
~*comme LIKE mais sans tenir compte de la casse
!~non décrit par l’expression régulière
!~*comme NOT LIKE mais sans tenir compte de la casse

Dans ce cas, la chaîne de caractères faisant l’objet du test est à gauche et correspond à une expression simple du type chaîne de caractères, il s’agit généralement d’un nom de colonne. L’expression régulière, qui s’écrit entre apostrophe simple, comme une chaîne de caractères, est située à droite de l’opérateur. La section 4.5.8 donne une description détaillée du formalisme des expressions régulières.

Un prédicat simple peut enfin correspondre à l’un des tests suivants :

expr IS NULLtest sur l’indétermination de expr
expr IN (expr_1 [, ...])comparaison de expr à une liste de valeurs
expr NOT IN (expr_1 [, ...])test d’absence d’une liste de valeurs
expr IN (requête)même chose, mais la liste de valeurs est le résultat d’une
expr NOT IN (requête)sous-requête qui doit impérativement retourner une table
 ne contenant qu’une colonne
EXIST (requête)vraie si la sous-requête retourne au moins un n-uplet
 vraie si au moins un n-uplet de la sous-requête vérifie la
expr operateur ANY (requête)comparaison « expr opérateur n-uplet » ; la sous-requête
 doit impérativement retourner une table ne contenant
 qu’une colonne ; IN est équivalent à = ANY
 vraie si tous les n-uplets de la sous-requête vérifient la
expr operateur ALL (requête)comparaison « expr opérateur n-uplet » ; la sous-requête
 doit impérativement retourner une table ne contenant
 qu’une colonne

Dans ce tableau, expr désigne une expression simple et requête une sous-requête.

Prédicat composé

Les prédicats simples peuvent être combinés au sein d’expression logiques en utilisant les opérateurs logiques AND (et logique), OR (ou logique) et NOT (négation logique).

4.5.8  Les expressions régulières

Introduction

Le terme expression régulière est issu de la théorie informatique et fait référence à un ensemble de règles permettant de définir un ensemble de chaînes de caractères.

Une expression régulière constitue donc une manière compacte de définir un ensemble de chaînes de caractères. Nous dirons qu’une chaîne de caractères est décrite par une expression régulière si cette chaîne est un élément de l’ensemble de chaînes de caractères défini par l’expression régulière.

PostgreSQL dispose de trois opérateurs de description par une expression régulière :

  1. LIKE ou ~~
  2. ~
  3. SIMILAR TO

La syntaxe et le pouvoir expressif des expressions régulières diffèrent pour ces trois opérateurs. Nous ne décrirons ici que la syntaxe du formalisme le plus standard et le plus puissant, celui que l’on retrouve sous Unix avec les commandes egrep, sed et awk. Ce formalisme est celui associé à l’opérateur ~.

Avec PostgreSQL, le test d’égalité avec une chaîne de caractères s’écrit :

expression='chaine'

De manière équivalente, le test de description par une expression régulière s’écrit :

expression~'expression_régulière'

L’opérateur de description ~ est sensible à la casse, l’opérateur de description insensible à la casse est ~*. L’opérateur de non description sensible à la casse est !~, son équivalent insensible à la casse se note !~*.

Formalisme

Comme nous allons le voir, dans une expression régulière, certains symboles ont une signification spéciale. Dans ce qui suit, expreg, expreg_1, expreg_2 désignent des expressions régulières, caractère un caractère quelconque et liste_de_caractères une liste de caractères quelconque.

caractère :

un caractère est une expression régulière qui désigne le caractère lui-même, excepté pour les caractères ., ?, +, *, {, |, (, ), ^, $, \, [, ]. Ces derniers sont des méta-caractères et ont une signification spéciale. Pour désigner ces méta-caractères, il faut les faire précéder d’un antislash (\., \?, \+, \*, \{, \|, \(, \), \^, \$, \\, \[, \]).

[liste_de_caractères] :
est une expression régulière qui décrit l’un des caractères de la liste de caractères, par exemple [abcdf] décrit le caractère a, le b, le c, le d ou le f ; le caractère - permet de décrire des ensembles de caractères consécutifs, par exemple [a-df] est équivalent à [abcdf] ; la plupart des méta-caractères perdent leur signification spéciale dans une liste, pour insérer un ] dans une liste, il faut le mettre en tête de liste, pour inclure un ^, il faut le mettre n’importe où sauf en tête de liste, enfin un - se place à la fin de la liste.
[^liste_de_caractères] :
est une expression régulière qui décrit les caractères qui ne sont pas dans la liste de caractères.
[:alnum:] :
à l’intérieur d’une liste, décrit un caractère alpha-numérique ([[:alnum:]] est équivalent à [0-9A-Za-z]) ; sur le même principe, on a également [:alpha:], [:cntrl:], [:digit:], [:graph:], [:lower:], [:print:], [:punct:], [:space:], [:upper:] et [:xdigit:].
. :
est une expression régulière et un méta-caractère qui désigne n’importe quel caractère.
^ :
est une expression régulière et un méta-caractère qui désigne le début d’une chaîne de caractères.
$ :
est une expression régulière et un méta-caractère qui désigne la fin d’une chaîne de caractères.
expreg? :
est une expression régulière qui décrit zéro ou une fois expreg.
expreg* :
est une expression régulière qui décrit expreg un nombre quelconque de fois, zéro compris.
expreg+ :
est une expression régulière qui décrit expreg au moins une fois.
expreg{n} :
est une expression régulière qui décrit expreg n fois.
expreg{n,} :
est une expression régulière qui décrit expreg au moins n fois.
expreg{n,m} :
décrit expreg au moins n fois et au plus m fois.
expreg_1expreg_2 :
est une expression régulière qui décrit une chaîne constituée de la concaténation de deux sous-chaînes respectivement décrites par expreg_1 et expreg_2.
expreg_1|expreg_2 :
est une expression régulière qui décrit toute chaîne décrite par expreg_1 ou par expreg_2.
(expreg) :
est une expression régulière qui décrit ce que décrit expreg.
\n :
n est un chiffre, est une expression régulière qui décrit la sous-chaîne décrite par la n ème sous-expression parenthèsée de l’expression régulière.
Remarque :
la concaténation de deux expressions régulières (expreg_1expreg_2) est une opération prioritaire sur l’union (expreg_1|expreg_2).

Exemples

Un caractère, qui n’est pas un méta-caractère, se décrit lui-même. Ce qui signifie que si vous cherchez une chaîne qui contient « voiture », vous devez utiliser l’expression régulière 'voiture'.

Si vous ne cherchez que les motifs situés en début de ligne, utilisez le symbole ^. Pour chercher toutes les chaînes qui commencent par « voiture », utilisez '^voiture'.

Le signe $ (dollar) indique que vous souhaitez trouver les motifs en fin de ligne. Ainsi : 'voiture$' permet de trouver toutes les chaînes finissant par « voiture ».

Le symbole . (point) remplace n’importe quel caractère. Pour trouver toutes les occurrences du motif composé des lettres vo, de trois lettres quelconques, et de la lettre e, utilisez : 'vo...e'. Cette commande permet de trouver des chaînes comme : voyagent, voyage, voyager, voyageur, vous_e.

Vous pouvez aussi définir un ensemble de lettres en les insérant entre crochets [ ]. Pour chercher toutes les chaînes qui contiennent les lettres P ou p suivies de rince, utilisez :'[Pp]rince'.

Si vous voulez spécifier un intervalle de caractères, servez-vous d’un trait d’union pour délimiter le début et la fin de l’intervalle. Vous pouvez aussi définir plusieurs intervalles simultanément. Par exemple [A-Za-z] désigne toutes les lettres de l’alphabet, hormis les caractères accentués, quelque soit la casse. Notez bien qu’un intervalle ne correspond qu’à un caractère dans le texte.

Le symbole * est utilisé pour définir zéro ou plusieurs occurrences du motif précédent. Par exemple, l’expression régulière '^Pa(pa)*$' décrit les chaînes : Pa, Papa, Papapa, Papapapapapapa, …

Si vous souhaitez qu’un symbole soit interprété littéralement, il faut le préfixer par un \. Pour trouver toutes les lignes qui contiennent le symbole $, utilisez : \$

Base de Données et langage SQL – Laurent Audibert

Previous Up Next