select

select

Nous attaquons alors les sujets de SQL (structured query language), et comment ce langage nous aide à implémenter l'algÚbre relationnelle !

Le rĂȘve : rĂ©cupĂ©rer uniquement les donnĂ©es nĂ©cessaires : pas plus, pas moins !!

... et d'ailleurs, aussi vite possible !

Commençons par la commande select.

À la base, SQL avait l'objectif de ressembler à un langage "naturel" (anglais) :

Select a-list-of-attributes from a-list-of-relations where attribute(s)-satisy-this-condition

Enfin, le rĂ©sultat est moins formel qu'un langage de programmation (on peut exprimer nos souhaits de plusieurs maniĂšres), mais il y a quand mĂȘme une structure obligatoire.

En revanche, SQL reste déclaratif : on exprime notre résultat désiré, et le SGBDR construit est implémenté un plan d'action pour nous retourner ce résultat.

La commande la plus simple, par exemple, pour extraire quelques attributs de la relation user de la base de données saas :

select gender, familyName, givenName from user;

Il est possible d'utiliser de caractĂšres de remplacement :

select * from user;

Mais, il ne faut jamais, jamais, jamais l'utiliser !

  • Vous risquez de divulguer les attributs sensibles !

  • Vous rĂ©cupĂ©rez tous les attributs, mĂȘme ceux dont on n’a pas besoin !

    • Saturer inutilement la bande-passante

    • Surcharger votre client, mĂ©moire du serveur, ou page web

    • Être facturĂ© (si service cloud qui facture la transmission des donnĂ©es)

Distinct

Regardez la requĂȘte suivante :

select gender, givenName from user order by givenName limit 10;

Le résultat est :

Pour extraire des tuples uniques (l'ensemble de colonnes est unique) on utilise le mot distinct :

select distinct gender, givenName 
from user 
order by givenName 
limit 10;

Notez qu'on voit deux fois le prénom Abdon parce que c'est l'ensemble des colonnes qui est utilisé pour déterminer si la ligne est unique (et dans ce cas, le gender est différent).

Opérations sur les attributs

On peut transformer les résultats au passage :

select upper(name), budget, budget * 0.2 as tva 
from action 
limit 10;

Ici :

  • on transforme le nom de chaque action en majuscule

  • on calcule le TVA du budget retournĂ© en multipliant par 20%. On renomme l'attribut avec le mot clĂ© as

Il y a une gamme de fonctions disponibles comme upper, selon l'implémentation, par exemple :

La clause where

Essayez toujours de minimiser la quantité de données retournée en filtrant les résultats avec la clause where :

select familyName, givenName 
from user 
where gender = 'female' and givenName like '%y%'
order by familyName desc 
limit 10;

Les opérateurs de comparaison sont :

  • = : Ă©gale Ă 

  • <> : pas Ă©gale Ă  (aussi, !=)

  • <=, <, >=, >

On combine les clauses pour créer des conditions complexes :

  • and : opĂ©rateur et (Ă©quivalent Ă  && en javascript)

  • or : opĂ©rateur ou (Ă©quivalent Ă  || en javascript)

  • not : opĂ©rateur nĂ©gation (Ă©quivalent Ă  ! en javascript)

Essayez de toujours filtrer les données autant possibles avant qu'elles arrivent coté client !

Utiliser des fonctions dans vos clauses where aussi !

Normalement, la comparaison entre deux attributs de type string est sensible au cas. Ceci peut changer selon le type de SGBDR et sa configuration.

Dans le doute, on peut utiliser les fonctions type UPPER ou LOWER pour normaliser la valeur Ă  un cas prĂ©cis. Par exemple, si on utilise l'adresse email comme identifiant, il faut qu'on puisse valider l'identitĂ© du compte malgrĂ© le bruit qui peut venir du client. Imaginons le cas oĂč l'utilisateur ne fait pas attention Ă  la capitalisation, et en plus, il y a du blanc dans le string envoyĂ© :

select familyName, givenName, email 
from user 
where 
  lower(email) = lower(trim("   ALIX.dupuy17@gmail.com "));

L'opérateur like

L’opĂ©rateur like permet le filtrage par motif :

select customerId, name from customer where name like "%dubois%";

Le pourcentage (%) agit comme un caractĂšre de remplacement (wildcard) pour plusieurs caractĂšres.

  • % : corresponde Ă  n’importe quel substring (chaĂźne de caractĂšres de n’importe quelle longueur)

  • _ : correspondre Ă  un seul caractĂšre

Par exemple :

  • like "_ _ _" : va trouver les textes de longueur 3 prĂ©cisĂ©ment

  • like "_ _ _%" : va trouver les strings d’au moins 3 caractĂšres

Comme sur UNIX, on peut Ă©chapper un pourcentage avec l'antislash \ si on cherche le caractĂšre % (ou _) :

like "ab\%cd"

Les valeurs null

Que faire quand il y a des valeurs manquantes ? Quel sens avec des opérateurs mathématiques ou en comparant avec des vraies valeurs ?

Par exemple, la phrase suivante veut dire quoi exactement ?

1 < null

Pour traiter ce cas un peu anormal, SQL utilise une valeur unknown. Donc :

  • A < null = unknown

  • true and null = unknown

  • false and null = false

  • true or null = true

  • false or null = unknown

  • not unknown = unknown

Utilisez null ou unknown comme critĂšres avec des clauses suivantes :

  • is null

  • is unknown

  • is not null

  • is not unknown

Par exemple :

select actionId, name, address_city
from action 
where address_city is not null 
limit 10;

Croiser plusieurs tables

Je veux récupérer la liste des intervenants avec leur nom, prénom et statut :

select givenName, familyName, status 
from user, animator 
where user.userId = animator.userId 
limit 10;

Notez le suivant de l'exemple ci-dessus :

  • Je peux sĂ©lectionner les attributs sans prĂ©ciser la table d'origine, car il n'y a pas d’ambiguĂŻtĂ©. Sinon, je serais obligĂ© de spĂ©cifier la table concernĂ©e (ex. user.givenName, et animator.status).

  • Je prĂ©cise la condition de jointure pour limiter le rĂ©sultat du produit cartĂ©sien.

Essayez la phrase suivante :

select givenName, familyName, status 
from user, animator 
limit 10;

Vous arrivez à expliquer le résultat ?

Je peux croiser plusieurs relations, mais parfois la phrase devient longue. Le mot clé as (pour créer des alias) sont pratiques pour plus facilement structurer las phrase :

select a.name as Action, c.name as Customer 
from action as a, customer as c, action_customer as ac 
where a.actionId = ac.actionId and ac.customerId = c.customerId 
limit 10;

Notez les 2 fonctions du mot clé as dans l'exemple :

  • Pour renommer les attributs dans le rĂ©sultat

  • Pour crĂ©er un alias pour plus facilement adresser les attributs dans les clauses where et select.

Le mot clé as : un outil puissant pour l'auto-comparaison !

Je veux savoir s'il existe des interventions qui sont facturĂ©es plus chĂšres que celles en Île-de-France.

Nous allons regarder la table action, notamment les champs budget et address_french_regionId.

Nous utilisons notre connaissance du produit cartĂ©sien pour faire cette requĂȘte ! Je sais qu'Ă  la base, on peut juste Ă©numĂ©rer toutes les combinaisons d'intervention possible avec :

select S.actionId, S.name, S.budget, S.address_french_regionId, A.actionId, A.name, A.budget, A.address_french_regionId
from action as S, action as A
limit 10;

Le mot clĂ© as me permet de traiter la mĂȘme relation comme deux relations diffĂ©rentes !

Le résultat est :

On voit dans le résultat (limité à 10 lignes) :

  • Sur la partie droite, on dĂ©multiplie l'action "Petit Acier Saucisses" avec toutes les actions sur le gauche.

  • Il suffit de chercher les lignes sur lesquelles :

    • le budget de la partie gauche est supĂ©rieur Ă  la partie droite

    • la rĂ©gion de la partie gauche n'est pas IDF (et pas nulle) et la rĂ©gion de la partie droite est IDF.

select S.actionId, S.name, S.budget, S.address_french_regionId, A.actionId, A.name, A.budget, A.address_french_regionId
from action as S, action as A
where 
  S.address_french_regionId != 'IDF' and S.address_french_regionId is not null 
  and 
  A.address_french_regionId = 'IDF' 
  and 
  S.budget > A.budget
limit 10;

Cette fois, on constate qu'on ne retient uniquement des valeurs du cĂŽtĂ© gauche qui satisfont les conditions. Mais, vu que les 2 relations sont dĂ©multipliĂ©es : pour chaque relation du gauche, son budget peut ĂȘtre supĂ©rieur Ă  plusieurs sur le cĂŽtĂ© droit :

Dans ce cas, il suffit d'extraire uniquement des interventions "uniques" qui répondent aux contraintes, avec le mot clé distinct (et en enlevant les attributs du cÎté droit) :

select distinct S.actionId, S.name, S.budget, S.address_french_regionId
from action as S, action as A
where 
  S.address_french_regionId != 'IDF' and S.address_french_regionId is not null 
  and 
  A.address_french_regionId = 'IDF' 
  and 
  S.budget > A.budget
limit 10;

Vous allez remarquer que si on enlĂšve la limit 10 Ă  la fin de la phrase, la requĂȘte prend beaucoup de temps.

Pourquoi ?

Notre relation action contient 32334 lignes. Pour construire le produit cartĂ©sien, il faut produire 32334*32334 lignes. Ceci peut ĂȘtre trĂšs long ! Il y a peut-ĂȘtre une meilleure façon de concevoir notre requĂȘte SQL pour rĂ©duire le temps d'exĂ©cution.

Exercice

Les exercices suivants sont basés sur la base de données SAKILA, une base de données de test fourni par MySQL.

Récupérez la base SAKILA ici.

Question 1

Trouvez la liste d’acteurs avec le prĂ©nom « Joe », et affichez seulement son ID, prĂ©nom et nom de famille. N’affichez que son ID, prĂ©nom et nom de famille.

Solution

1 résultat.

select actor_id, first_name, last_name 
from actor 
where first_name = "Joe";

Question 2

Trouvez tous les acteurs dont le nom de famille contient les lettres « gen » N’affichez que son ID, prĂ©nom et nom de famille.

Solution

4 résultats.

select actor_id, first_name, last_name 
from actor 
where upper(last_name) like "%GEN%";

Question 3

Trouvez tous les acteurs dont le nom de famille contient les lettres « li », et trier par nom de famille puis par prĂ©nom. N’affichez que son ID, prĂ©nom et nom de famille.

Solution

10 résultats.

select actor_id, first_name, last_name
from actor 
where upper(last_name) like "%LI%" 
order by last_name, first_name;

Question 4

Cherchez tous les pays qui ont une ville dont le nom commence par « San ». N’affichez que le nom du pays, et enlevez les duplications.

Solution

10 pays.

select distinct(country) 
from country, city 
where 
  country.country_id = city.country_id 
  and city like "San%";

Question 5

Trouvez tous les films dans lequel joue l’acteur « Nick Wahlberg »

Solution

25 films.

select title 
from film, film_actor, actor
where 
  actor.actor_id = film_actor.actor_id 
  and film_actor.film_id = film.film_id 
  and actor.first_name = "NICK" 
  and actor.last_name = "WAHLBERG";

Question 6

Trouvez tous les films de type « comedy » avec un rating de « PG » ou « G ».

Solution

27 films.

select title
from film, film_category, category
where 
  film.film_id = film_category.film_id 
  and film_category.category_id = category.category_id 
  and category.name = "Comedy" 
  and (film.rating = "G" or film.rating = "PG");

DerniĂšre mise Ă  jour