Données SQL Interrogation

Identification

Infoforall

24 - SQL : SELECT FROM WHERE


Drop Table ?

Prerequis : la première activité sur les Bases de Données.

Documents de cours : open document ou pdf

1 - DB Brownser

Intro-01° Télécharger et installer le logiciel DB Brownser si ce n'est pas fait.

Il s'agit d'un Système de Gestion de Bases de Données (SGBD) léger, open source, portable (on peut l'installer sur une clé USB) mais ne gérant que SQLite.

https://sqlitebrowser.org/

Logo :

Logo DB Brownser

Intro-02° Télécharger la base de données contenant trois relations :

  • Une relation titre qui contient des informations sur les films, les séries et autres productions
  • Une relation personne qui contient des informations sur personnes ayant travaillé sur les productions précédentes
  • Une relation emploi qui contient des informations sur les emplois occupés et qui possède donc des clés secondaires menant à des n-uplets de titre et personne.

Il faut vous connecter pour voir les liens de téléchargement.

Sinon, vous pouvez me contacter via le bouton CONTACT en bas de page.

Intro-03° Ouvrir ce fichier via DB Browser. Vous pouvez soit cliquer dessus, soit ouvrir le logiciel puis ouvrir votre fichier à l'intérieur du logiciel.

Vous devriez aboutir à cela :

DB Brownser

Nous allons travailler aujourd'hui sur une base de données créée à partir de fichiers CSV d'un site de notation de films, vidéos et séries : https://www.imdb.com/. J'ai réalisé la conversion en 2020. Il manque donc des films.

Le lien vers les fichiers CSV mis à jour est https://www.imdb.com/interfaces/.

Subsets of IMDb data are available for access to customers for personal and non-commercial use. You can hold local copies of this data, and it is subject to our terms and conditions. Please refer to the Non-Commercial Licensing and copyright/license and verify compliance.

2 - Projection : SELECT a FROM b

SQL

SQL veut dire Structured Query Language.

Cela veut dire Langage de Requête Structurée.

C'est un langage déclaratif qui permet d'interroger la base de données qui fournit alors une réponse et nous la transmet.

SQL est donc un langage où nous allons devoir placer des informations à certains endroits particuliers. Le tout est donc de connaître les mots-clés et de savoir ce qu'on doit y placer derrière.

Pas de CREATE TABLE au BAC

Nous avions vu dans l'activité précédente les instructions permettant de créer une relation dans la base de données. Vous n'avez pas à les connaître pour l'épreuve du BAC . J'en parle pour que vous sachiez que c'est possible : on ne crée que rarement des relations en cliquant sur les boutons d'un logiciel mais surtout en fournissant les instructions. L'épreuve porte sur l'utilisation de tables déjà existantes.

Voici à titre d'exemple, la commande SQL qui permet de créer la table titre.

Déclaration du schéma relationnel de la table (implémentation SQL)

1 2 3 4 5 6 7 8 9 10 11 12
CREATE TABLE titre ( id INTEGER UNIQUE, tconst VARCHAR(9), title_type VARCHAR(15), primary_title VARCHAR(40), original_title VARCHAR(40), start_year INTEGER, end_year INTEGER, runtime_minutes INTEGER, genres TEXT, PRIMARY KEY(tconst) );

Remarque 1 (L3-L11) : PRIMARY KEY fait de tconst la clé primaire de cette table. Elle est donc définie comme NOT NULL et UNIQUE (qui veut dire qu'elle doit être renseignée et qu'elle ne peut pas valoir une valeur déjà attribuée sur un autre n-uplet).

Remarque 2 (L2) : UNIQUE implique que la BDD ne va pas accepter une nouvelle entrée qui aurait la même valeur. Cela permet de respecter l'unicité. Il y a un cas particulier qui n'a pas à respecter cette unicité : la valeur NULL (qui veut dire "non connue" ou "non renseignée") peut se trouver présente plusieurs fois pour cet attribut (cette "colonne"). L'attribut id n'est pas ici une clé primaire mais pourrait faire office de clé primaire si on rajoutait NOT NULL. On dirait que c'est une clé secondaire.

Remarque 3 : VARCHAR(40) veut dire qu'on crée un attribut de type texte qui pourra prendre jusqu'à 40 caractères. Noter juste CHAR(40) implique que l'attribut fera exactement 40 caractères. De toutes manières, ici SQLite transformera le tout en TEXT à l'interne...

Remarque 4 : ayant récupéré cette base de donnée, je n'ai pas décidé des valeurs de la clé primaire. Il s'agit ici d'un texte mais la plupart du temps, il s'agit d'un nombre et l'attribut se nomme id. C'est pour cela que je l'ai placé en première position. Mais attention, ici, la clé primaire est bien tconst

Déclaration du schéma relationnel de la table (version abstraite)

1 2 3 4 5 6 7 8 9 10 11
titre ( id INTEGER, tconst VARCHAR(9), title_type VARCHAR(15), primary_title VARCHAR(40), original_title VARCHAR(40), start_year INTEGER, end_year INTEGER, runtime_minutes INTEGER, genres TEXT )

Rappel : on souligne la clé primaire dans les schémas relationnels.

Les noms sont relativement explicites mais voici quelques précisions :

  1. id : un numéro d'enregistrement que j'ai rajouté et qui aurait pu servir également de clé primaire
  2. tconst : le texte servant de clé primaire pour la table titre.
  3. title_type : un texte permettant de savoir si c'est un film, une série...
  4. primary_title : un texte qui est le titre sous lequel cette œuvre est connue (la base de données est celle d'un site américain)
  5. original_title : le titre originel de l'œuvre
  6. start_year et end_year : ce sont la date de sortie et la date d'arrêt. end_year est surtout utile pour les séries. Un film se termine normalement la même année que sa sortie...
  7. runtime_minutes : la durée en minutes.
  8. genres : un texte donnant le ou les genres de l'œuvre. Exemple : Action, Crime, Drama.
Exécuter du SQL avec DB Brownser
Avec DB Brownser :

  1. On va dans EXECUTER LE SQL
  2. On tape la requête SQL voulue.
  3. On exécute en appuyant sur le bouton FLECHE.
2.1 Projection avec SELECT attributs FROM table

Choix d'attributs

L'opération de projection consiste à récupérer un sous-ensemble des attributs.

Cela revient presque à sélectionner des "colonnes" dans un fichier CSV visualisé via un tableur.

1 2
SELECT primary_title, runtime_minutes, genres FROM titre;

TRADUCTION

Projette le titre, la durée et le genre
à partir des n-uplets présents dans la table titre.

Exemple

Si on veut récupérer les noms, les durées et les genres des titres présents dans la table ci-dessous, cela revient à récupérer la sélection rouge, plusieurs 3-uplets.

Table titre avec nom, durée et genres sélectionnés
Sélection du nom, de la durée et des genres
Résultat : table titre avec nom, durée et genres sélectionnés
Ordre des attributs dans SELECT

Attention, l'ordre fourni a une influence sur la projection. Si on inverse le genre et la durée, on obtient bien ces attributs dans l'ordre fourni.

Résultat : table titre avec nom, genres et durée sélectionnés

01° Fournir la requête SQL permettant de récupérer le nom, le numéro d'enregistrement et la date de création pour tous les n-uplets présents dans la relation titre.

...CORRECTION...

SELECT primary_title, id, start_year FROM titre

Voici le code SQL permettant de créer la table personne qui contient les informations sur les différentes personnes travaillant sur ces réalisations :

1 2 3 4 5 6 7 8 9 10
CREATE TABLE personne ( id INTEGER UNIQUE, nconst VARCHAR(12), name VARCHAR(50), birth_year INTEGER CHECK(birth_year = 0 OR (birth_year > 1000 AND birth_year < 3000)), death_year INTEGER CHECK(death_year = 0 OR (death_year > 1000 AND death_year < 3000)), primary_profession TEXT, known_for_titles TEXT, PRIMARY KEY(nconst) );

Le schéma relationnel de la table personne :

1 2 3 4 5 6 7 8 9
personne ( id INTEGER, nconst VARCHAR(12), name VARCHAR(50), birth_year INTEGER, death_year INTEGER, primary_profession TEXT, known_for_titles TEXT )

02° Fournir la requête SQL permettant de récupérer le nom, la date de naissance et la date de mort pour tous les n-uplets présents dans la relation personne.

...CORRECTION...

SELECT name, birth_year, death_year
FROM personne
2.2 Clause DISTINCT (Hors programme je pense)

Une opération assez commune consiste à trouver les différentes valeurs attribuées à un attribut. Comme la même valeur peut être présente plusieurs fois, on aurait à faire le tri si on recevait toutes les valeurs présentes. Mais on peut demander à SQL de ne fournir chaque valeur qu'une seule et unique fois.

Pour ramener un unique exemplaire des différentes valeurs qu'on trouve dans un attribut :

1 2
SELECT DISTINCT title_type FROM titre;

TRADUCTION

Projette un unique exemplaire du type de production
à partir des n-uplets de la table titre.

SELECT DISTINCT title_type
FROM titre

Comme vous pouvez le voir, chaque valeur d'attribut n'apparaît qu'une seule fois. Sans le mot-clé DISTINCT nous aurions eu chaque entrée plusieurs fois, et autant de lignes que de n-uplets.

3 - Filtrage avec WHERE c

Passons maintenant à des requêtes plus fines.

Vous savez projeter uniquement certains attributs ("colonnes") avec SELECT.

Vous allez maintenant voir comment ne récupérer que certains n-uplets ("lignes") avec WHERE.

Pour cela, il faut falloir dire (dans cet ordre, attention) :

  1. Quels sont les attributs qu'on veut récupérer (SELECT)
  2. Depuis quelle table (FROM)
  3. Quels sont les n-uplets qu'on veut filtrer parmi tous les n-uplets disponibles (WHERE)
3.1 Clause WHERE

Quelques opérateurs booléens

La clause WHERE permet de filtrer la projection en n'acceptant que certains n-uplets (certaines "lignes").

On y utilise des opérateurs de comparaisons avec la syntaxe suivante :

  • Egalité : = (contrairement à == en Python)
  • Différence : <> (contrairement à != en Python)
  • Autres comparaisons : <, <=, >, >=
  • Opérateurs logiques : AND, OR, NOT
  • Opérateurs arithmétiques : +, -, *, /, %
1er exemple

On veut récupérer les titres, l'année de sortie et les durées des titres de 2020.

1 2 3
SELECT primary_title, start_year, runtime_minutes FROM titre WHERE start_year = 2020;

TRADUCTION

Projette le titre, l'année de sortie et la durée
à partir des n-uplets de la table titre
qui sont sortis en 2020.

x
2e exemple

On veut récupérer les titres qui n'ont pas commencé en 2020 et dont la durée est inférieure à 2h.

1 2 3
SELECT primary_title, start_year, runtime_minutes FROM titre WHERE start_year <> 2020 AND runtime_minutes < 120;

TRADUCTION

Projette le titre, l'année de sortie et la durée
à partir des n-uplets de titre
(qui ne sont pas sortis en 2020) et (qui durent moins de 2h).

Le résultat :

x
Deux remarques
  1. On voit que le fait que certaines entrées soient "vides" pose problème : "Noblesse Oblige" apparaît alors que le titre n'a pas de date de création ! Puisqu'on ne connaît pas la valeur, il est possible que ce soit 2020 ! C'est l'intêret de placer NULL normalement : le n-uplet n'aurait pas été sélectionné.
  2. On peut très bien ne pas mettre dans le SELECT qu'une partie des attributs du n-uplets qu'on va ramener avec FROM, même s'ils n'interviennent pas dans la clause WHERE. La requête ci-dessous est légitime et ne projette que la liste des titres alors que le filtrage se fait sur la durée :
  3. 1 2 3
    SELECT primary_title FROM titre WHERE start_year <> 2020 AND runtime_minutes < 120;

03° Fournir la requête permettant d'obtenir les titres ayant commencé entre 2010 et 2012.

...CORRECTION...

1 2 3
SELECT primary_title FROM titre WHERE start_year >= 2010 AND start_year <= 2012;

Le résultat visuel :

x

04° Fournir la requête permettant d'obtenir les séries ayant commencé après 2018.

Rappel d'une des réponses précédentes :

SELECT DISTINCT title_type
FROM titre

...CORRECTION...

1 2 3
SELECT primary_title, start_year, end_year FROM titre WHERE title_type = "tvSeries" AND start_year > 2018;

Le résultat visuel (sans la date de début, j'avais oublié):

x

05° Fournir la requête permettant d'obtenir les séries finies ayant duré au moins 10 ans.

Comme les attributs de date sont des entiers, une date non fournie équivaut à un 0.

...CORRECTION...

1 2 3
SELECT primary_title, start_year, end_year FROM titre WHERE title_type = "tvSeries" AND end_year <> 0 AND (end_year - start_year) >= 10;

Le résultat visuel :

x
3.2 Priorités du AND sur le OR

a AND b est plus ou moins équivalent à une multiplication dans l'algèbre de Boole.

On peut considérer que a AND b = a.b.

a OR b est plus ou moins équivalent à une addition dans l'algèbre de Boole.

Dans l'algèbre de Boole où FAUX est 0 et VRAI est tout le reste, on peut considérer que a OR b = a + b

Dans l'algèbre de Boole où FAUX est 0 et VRAI est 1, le domaine est uniquement {0, 1}, on doit donc gérer le cas "1+1" qui ne peut pas donner 2. On peut alors considérer que a OR b = a + b - a.b

Le AND est donc prioritaire sur OR.

06° Voici deux requêtes permettant de récupérer les titres qui correspondent à une sortie entre 2010 et 2015.

Les deux requêtes sont-elles similaires ?

Sans les parenthèses :

1 2 3
SELECT primary_title, start_year, end_year FROM titre WHERE start_year >= 2010 AND start_year <= 2015 OR end_year >= 2010 AND end_year <= 2015;

Avec les parenthèses :

1 2 3
SELECT primary_title, start_year, end_year FROM titre WHERE (start_year >= 2010 AND start_year <= 2015) OR (end_year >= 2010 AND end_year <= 2015);

...CORRECTION...

Les deux requêtes sont bien similaires puisque le AND est prioritaires sur le OR : même sans les parenthèses, on commence par évaluées les AND et ensuite on utilise le OU.

4 - Commande ORDER BY

4.1 Trier les résultats avec ORDER BY (Hors programme je pense)

Principe

La commande ORDER BY permet de trier les n-uplets (lignes) d’une réponse. On pourra trier les données sur un ou plusieurs attributs (colonnes), et par ordre croissant ou décroissant.

ORDER BY ...

ORDER BY ... ASC

ORDER BY ... DESC

Tri simple croissant
1 2 3 4
SELECT primary_title, start_year FROM titre WHERE title_type = "movie" ORDER BY start_year;
x

On remarquera que cette commande est équivalente à la suivante où on utilise le suffixe ASC : c'est l'option par défaut.

1 2 3 4
SELECT primary_title, start_year FROM titre WHERE title_type = "movie" ORDER BY start_year ASC;
4.2 Trier les résultats avec ORDER BY en ordre décroissant (Hors programme je pense)

Tri en ordre décroissant avec

On précise alors le suffixe DESC.

1 2 3 4
SELECT primary_title, start_year FROM titre WHERE title_type = "movie" ORDER BY start_year DESC;
x

Zut ! Les entrées vides sont ici considérées comme supérieures aux autres ! Pourquoi ? Car les zones "vides" ne sont pas bien définies. Nous verrons plus tard comment les modifier avec UPDATE.

Nous allons rajouter une petite condition pour supprimer les entrées incorrectes dont les dates sont mal renseignées :

1 2 3 4
SELECT primary_title, start_year FROM titre WHERE title_type = "movie" AND start_year <> '' ORDER BY start_year DESC;
x
4.3 Trie sur plusieurs colonnes avec ORDER BY (encore plus Hors programme je pense)

Tri multiple

Comme vous pouvez le voir ci-dessous, les n-uplets sont triés par date mais apparaissent dans l'ordre d'inscription dans la base de données. Si vous voulez les trier par date puis par ordre alphabétique, il suffit de rajouter l'attribut primary_title après celui de la date.

1 2 3 4
SELECT primary_title, start_year FROM titre WHERE title_type = "movie" AND start_year <> '' ORDER BY start_year DESC, primary_title ASC;
x

07° Reprendre la table personne. Fournir la requête permettant de récupérer les personnes nées entre 1970 et 1980 en les triant par ordre alphabétique.

1 2 3 4 5 6 7 8 9 10
CREATE TABLE personne ( id INTEGER UNIQUE, nconst VARCHAR(12), name VARCHAR(50), birth_year INTEGER CHECK(birth_year = 0 OR (birth_year > 1000 AND birth_year < 3000)), death_year INTEGER CHECK(death_year = 0 OR (death_year > 1000 AND death_year < 3000)), primary_profession TEXT, known_for_titles TEXT, PRIMARY KEY(nconst) );

On notera, qu'ici, on utilise 0 pour signaler l'absence d'information. Encore une fois, NULL aurait sans doute était préférable.

...CORRECTION...

1 2 3 4
SELECT name FROM personne WHERE birth_year >= 1970 AND birth_year <= 1980 ORDER BY name;

On aurait alors juste un affichage de ce type :

x

08° Reprendre la table titre. Fournir la requête permettant de récupérer les films de 2020 (et uniquement les films) en les triant par durée, du plus long au moins long.

1 2 3 4 5 6 7 8 9 10 11 12
CREATE TABLE titre ( id INTEGER UNIQUE, tconst VARCHAR(9), title_type VARCHAR(15), primary_title VARCHAR(40), original_title VARCHAR(40), start_year INTEGER, end_year INTEGER, runtime_minutes INTEGER, genres TEXT, PRIMARY KEY(tconst) );

...CORRECTION...

1 2 3 4
SELECT primary_title, runtime_minutes, title_type FROM titre WHERE start_year = 2020 and title_type = 'movie' ORDER BY runtime_minutes DESC;

On aurait alors juste un affichage de ce type :

x

5 - Fonctions d'agrégation

On peut également facilement réaliser des calculs sur l'ensemble des données.

5.1 MAX()

Recherche de maximum

C'est assez étrange au premier abord : on ne place pas cette recherche de maximum dans le WHERE.

C'est normal, ce n'est pas un filtrage mais un calcul. Il est bien directement dans le SELECT puisque c'est cela qu'on va projeter. Le SGBD fournit alors le premier n-uplet trouvé qui correspond à ce maximum. Attention, avec cette façon d'agir, on récupère juste la première occurrence en cas d'égalité. Il est donc possible qu'il y ai plusieurs n-uplets ayant cette valeur maximale. Le n-uplet récupéré est juste le premier dans la liste.

Pour trouver la durée de film la plus longue :

1 2 3
SELECT primary_title, MAX(runtime_minutes) FROM titre WHERE title_type = 'movie';

TRADUCTION

Projette le premier titre qui possède la plus grande durée et cette durée maximale obtenue
à partir des n-uplets de titre
qui sont des films.

x
5.2 MIN()

Recherche de minimum

Pour trouver la durée de film la plus courte et le premier n-uplet correspondant :

1 2 3
SELECT primary_title, MIN(runtime_minutes) FROM titre WHERE title_type = 'movie';

TRADUCTION

Projette le premier titre qui possède la plus petite durée, et cette durée minimale obtenue
à partir des n-uplets de la table titre
qui sont des films.

x
5.3 AVG()

Calcul de moyenne

Pour trouver la durée moyenne des films :

1 2 3
SELECT AVG(runtime_minutes) FROM titre WHERE title_type = 'movie';

TRADUCTION

Projette la durée moyenne calculée
à partir des n-uplets de titre
qui sont des films.

x
5.4 SUM()

Calcul de la somme des valeurs d'un attribut

Pour trouver la durée totale des films produits en 2020 :

1 2 3
SELECT SUM(runtime_minutes) AS total_en_minutes FROM titre WHERE title_type = 'movie' and start_year = 2020;

TRADUCTION

Projette la somme des durées calculée
à partir des n-uplets de titre
qui sont des films sortis en 2020.

x
ATTENTION : parenthèses et un seul retour

Fonction donc parenthèses

Ce sont des fonctions, n'oubliez pas vos parenthèses.

Un seul n-uplet renvoyé

Dès qu'on utilise l'une des fonctions d'agrégation, SQL va vous renvoyer un unique n-uplet. Si vous cherchez la valeur minimale, vous récupérer cette valeur et le premier p-uplet ayant cette valeur. Le n-uplet récupéré n'a donc pas spécialement d'importance, sauf s'il est vraiment l'unique plus petit. Avec les films, ça ne marche pas par exemple : il y a plusieurs films de 35 minutes.

Si vous voulez récupérer la liste de tous les films les plus courts, il faut le faire en deux fois :

  • D'abord, trouver la valeur minimale
  • Ensuite, l'utiliser pour faire une requête pour récupérer tous les films qui ont cette durée.

09° Fournir la requête permettant de récupérer la durée moyenne des sorties des années 2010 à 2020.

1 2 3 4 5 6 7 8 9 10 11 12
CREATE TABLE titre ( id INTEGER, tconst VARCHAR(9) UNIQUE, title_type VARCHAR(15), primary_title VARCHAR(40), original_title VARCHAR(40), start_year INTEGER, end_year INTEGER, runtime_minutes INTEGER, genres TEXT, PRIMARY KEY(tconst) );

...CORRECTION...

1 2 3
SELECT AVG(runtime_minutes) AS moyenne FROM titre WHERE start_year >= 2010 AND start_year <= 2020;

On aurait alors juste un affichage de ce type :

x
5.5 Obtenir le nombre d'occurences avec COUNT()

Cette fonction permet de compter le nombre de n-uplets renvoyés par notre requête.

Pour l'exemple, voici le nombre de films parus en 2000.

1 2 3
SELECT start_year, COUNT(*) AS nombre FROM titre WHERE start_year = 2000;

TRADUCTION

Projette l'année de départ recherchée et le nombre de n-uplets récupérés
à partir des n-uplets de titre
qui sont sortis en 2000.

x

Etoile ?

L'étoile est utilisée comme le caractère magique qui remplace n'importe quel autre. On peut l'utiliser car de toutes manières, quelque soit l'attribut qui servira à compter le nombre de lignes de n-uplets récupérés, on obtiendra toujours le même nombre.

10° Fournir la requête permettant de compter le nombre de titres de plus de 180 minutes.

...CORRECTION...

1 2 3
SELECT COUNT(*) AS nombre FROM titre WHERE runtime_minutes > 180;

On aurait alors juste un affichage de ce type :

x
Pour aller plus loin : SELECT de SELECT (hors programme, c'est certain)

Ce n'est pas une compétence attendue au BAC.

On peut également utiliser le résultat d'une première projection pour en obtenir une deuxième.

On peut ainsi faire ceci en une seule requête :

  • D'abord, trouver la valeur minimale
  • Ensuite, l'utiliser pour faire une requête pour récupérer tous les films qui ont cette durée.

Voici un exemple où on recherche TOUS les puplets dont la durée est la durée minimale.

1 2 3 4 5 6 7
SELECT primary_title, runtime_minutes, start_year FROM titre WHERE runtime_minutes = ( SELECT MIN(runtime_minutes) FROM titre WHERE title_type = 'movie' );
x

Vous auriez également pu mettre tout ceci sur une seule ligne, mais est-ce plus clair ?

1 2 3
SELECT primary_title, runtime_minutes, start_year FROM titre WHERE runtime_minutes = (SELECT MIN(runtime_minutes) FROM titre WHERE title_type = 'movie');
Pour aller plus loin : WITH (hors programme, c'est certain)

Ce n'est pas une compétence attendue au BAC.

On peut également plutôt mémoriser un résultat intermédiaire et l'utiliser ensuite.

1 2 3 4 5 6 7
SELECT primary_title, runtime_minutes, start_year FROM titre WHERE runtime_minutes = ( SELECT MIN(runtime_minutes) FROM titre WHERE title_type = 'movie' );
x

Vous auriez également pu mettre tout ceci sur une seule ligne, mais est-ce plus clair ?

1 2 3
SELECT primary_title, runtime_minutes, start_year FROM titre WHERE runtime_minutes = (SELECT MIN(runtime_minutes) FROM titre WHERE title_type = 'movie');

6 - LIKE et IN

Dernière chose que nous allons voir : comment retrouver les n-uplets

  • dont l'un des champs "ressemble" à ce qu'on cherche (LIKE) ou
  • dont les valeurs sont dans un ensemble bien précis (IN)

Pourquoi en parler alors que ce n'est pas au programme ? Simplement car c'est vraiment pratique.

Mot-clés à savoir utiliser lors de l'épreuve écrite de NSI
  • SELECT, FROM, WHERE (cette activité)
  • JOIN ON (la prochaine)
  • UPDATE, INSERT, DELETE (la dernière)

On peut sans doute rajouter AND et OR puisqu'ils se comportent comme leurs équivalents en Python.

Cherchons par exemple les films qui contiennent le mot 'alien'.

6.1 Ressemblance avec LIKE (hors programme)

On utilise LIKE dans la clause WHERE. Cela permet de rechercher un modèle de réponse plutôt qu'une recherche précise.

Le caractère % est le caractère joker qui remplace tous les autres : % peut être n'importe quelle chaîne de caractères.

1 2 3
SELECT * FROM titre WHERE primary_title LIKE '%alien %' or '%aliens%';

TRADUCTION

Projette les n-uplets parmi
les n-uplets de la table titre
dont le titre contient le texte "alien " ou le texte "aliens".

x

11° Fournir la requête permettant de récupérer les p-uplets de films dont le genre contient le genre "Sci-Fi".

1 2 3 4 5 6 7 8 9 10 11 12
CREATE TABLE titre ( id INTEGER UNIQUE, tconst VARCHAR(9), title_type VARCHAR(15), primary_title VARCHAR(40), original_title VARCHAR(40), start_year INTEGER, end_year INTEGER, runtime_minutes INTEGER, genres TEXT, PRIMARY KEY(tconst) );

...CORRECTION...

1 2 3
SELECT * FROM titre WHERE genres LIKE '%Sci-Fi%' and title_type = 'movie';

On aurait alors juste un affichage de ce type :

x

12° Fournir la requête permettant de récupérer les p-uplets de films dont le genre contient le genre "Fantasy" du plus récent au plus ancien et dans l'ordre alphabétique.

1 2 3 4 5 6 7 8 9 10 11 12
CREATE TABLE titre ( id INTEGER UNIQUE, tconst VARCHAR(9), title_type VARCHAR(15), primary_title VARCHAR(40), original_title VARCHAR(40), start_year INTEGER, end_year INTEGER, runtime_minutes INTEGER, genres TEXT, PRIMARY KEY(tconst) );

...CORRECTION...

1 2 3 4
SELECT * FROM titre WHERE genres LIKE '%Fantasy%' and title_type = 'movie' and start_year <> '' ORDER BY start_year DESC, primary_title;

On aurait alors juste un affichage de ce type :

x
6.2 Appartenance avec IN (proche de Python, hors programme ?)

On utilise IN pour vérifier l'appartenance d'un élément à une liste définie d'éléments.

Par exemple, pour obtenir les distributions qui sont soit un film ou une série, on peut utiliser un OR ou un IN :

Version avec un OR

1 2 3
SELECT primary_title, start_year, end_year FROM titre WHERE title_type = "tvSeries" OR title_type = "movie";

TRADUCTION

Projette le titre, la date de sortie et la date d'arrêt
des n-uplets de la table titre
dont le genre est film ou dont le genre est série.

Version avec un IN

1 2 3
SELECT primary_title, start_year, end_year FROM titre WHERE title_type IN ("tvSeries", "movie");

TRADUCTION

Projette le titre, la date de sortie et la date d'arrêt
des n-uplets de la table titre
dont le genre est parmi la sélection suivante : (film, série).

7 - Python et CSV

On trouve parfois des exercices qui mélangent connaissances en SQL et comparaison avec un code Python agissant sur des données stockées dans des tableaux de dictionnaire. Il est donc temps de voir comment nous pourrions projeter et filtrer avec ces tableaux de dictionnaires Python.

13° Lancer le programme fourni dans la partie ci-dessous. Vérifier ensuite que vous parveniez à le comprendre en cherchant à suivre son déroulé comme le ferez l'interpréteur Python. Posez des questions en cas de problème.

7 Python et ses tableaux dynamiques de dictionnaires ?

Imaginons qu'on soit parvenu à lire un fichier CSV et qu'on ai créé les informations sur les jeux et les consoles dans deux tableaux de dictionnaires.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
# Déclaration des fonctions def projeter(table:list[dict], attributs_voulus:list[str]) -> list[dict]: """Projette les attributs voulus dans la table fournie""" projection = [] # Création d'un tableau dynamique for nuplet in table: # pour chaque n-uplet de la table copie_nuplet = {} # nouveau n-uplet nommé qui va stocker la réponse partielle for cle, valeur in nuplet.items(): # pour chaque clé et valeur associée dans le n-uplet de la table if cle in attributs_voulus: # si le nom de la clé apparait dans le tableau des attribus voulus copie_nuplet[cle] = valeur # on rajoute cette association clé-valeur dans la projection projection.append(copie_nuplet) # on rajoute le nouveau n-uplet dans la projection return projection # on renvoie le tableau contenant les copies tronquées des n-uplets def uniquement_plateformes(table:list[dict]) -> list[dict]: """Filtre les n-uplets de la table en ne renvoyant que ceux dont le genre est bien plateformes""" reponse = [] # Création d'un tableau dynamique for nuplet in table: # pour chaque n-uplet de la table if nuplet['j_genre'] == 'Plate-formes': # si le genre de ce n-uplet est un jeu de plate-formes copie_nuplet = {} # va permettre de copier les dictionnaires (qui sont fortement muables) for cle, valeur in nuplet.items(): # pour chaque cle-valeur associée dans le n-uplet à garder copie_nuplet[cle] = valeur # on copie la même chose dans le dictionnaire de copie reponse.append(copie_nuplet) # on rajoute la copie dans notre tableau de réponse return reponse # on renvoie le tableau contenant les copies des n-uplets voulus # PROGRAMME DE TEST if __name__ == '__main__': jeu = [ {'j_id':7, 'j_jeu':'Flight Simulator', 'j_description':"Simulation d'avion", 'j_sortie':1980, 'j_éditeur':'subLOGIC', 'j_genre':'Simulateur', 'j_id_support':1}, {'j_id':8, 'j_jeu':'DONKEY KONG', 'j_description':"Un méchant singe géant", 'j_sortie':1981, 'j_éditeur':'Nintendo', 'j_genre':'Plate-formes', 'j_id_support':None}, {'j_id':9, 'j_jeu':'PIFALL!', 'j_description':"Pitfall Harry, un explorateur", 'j_sortie':1982, 'j_éditeur':'Activision', 'j_genre':'Plate-formes', 'j_id_support':2}, {'j_id':10, 'j_jeu':'PIFALL!', 'j_description':"Pitfall Harry, un explorateur", 'j_sortie':1983, 'j_éditeur':'Nintendo', 'j_genre':'Plate-formes', 'j_id_support':3}, {'j_id':11, 'j_jeu':'Boulder Dash', 'j_description':"Rockford, mineur téméraire", 'j_sortie':1983, 'j_éditeur':'First Star Software', 'j_genre':'Plate-formes', 'j_id_support':3} ] console = [ {'c_id':1, 'c_nom':'Apple II', 'c_RAM':'4 ko', 'c_date_sortie':'1977-06-10'}, {'c_id':2, 'c_nom':'Atari 2600', 'c_RAM':'4 ko', 'c_date_sortie':'1977-10-14'}, {'c_id':3, 'c_nom':'C64', 'RAM':'c_64 ko', 'c_date_sortie':'1982-08-01'} ] print("Exemple de projection") nouvelle_table = projeter(jeu, ['j_jeu', 'j_description']) for nuplet in nouvelle_table: # pour chaque n-uplet de la jointure print(nuplet) # affiche ce n-uplet print("\n\nExemple de filtrage") nouvelle_table = uniquement_plateformes(jeu) for nuplet in nouvelle_table: # pour chaque n-uplet de la jointure print(nuplet) # affiche ce n-uplet

14° Exercice à trou type "épreuve pratique.

Compléter les codes.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
# Déclaration des fonctions def projeter(table:list[dict], attributs_voulus:list[str]) -> list[dict]: """Projette les attributs voulus dans la table fournie""" projection = [] # Création d'un tableau dynamique for ... in ...: # pour chaque n-uplet de la table copie_nuplet = {} # nouveau n-uplet nommé qui va stocker la réponse partielle for cle, valeur in ... ...: # pour chaque clé et valeur associée dans le n-uplet de la table if ... in ...: # si le nom de la clé apparait dans le tableau des attribus voulus ... # on rajoute cette association clé-valeur dans la projection ... # on rajoute le nouveau n-uplet dans la projection return projection # on renvoie le tableau contenant les copies tronquées des n-uplets def uniquement_plateformes(table:list[dict]) -> list[dict]: """Filtre les n-uplets de la table en ne renvoyant que ceux dont le genre est bien plateformes""" reponse = [] # Création d'un tableau dynamique ... # pour chaque n-uplet de la table if ... # si le genre de ce n-uplet est un jeu de plate-formes copie_nuplet = {} # va permettre de copier les dictionnaires (qui sont fortement muables) ... # pour chaque cle-valeur associée dans le n-uplet à garder copie_nuplet[cle] = valeur # on copie la même chose dans le dictionnaire de copie ... # on rajoute la copie dans notre tableau de réponse return reponse # on renvoie le tableau contenant les copies des n-uplets voulus # PROGRAMME DE TEST if __name__ == '__main__': jeu = [ {'j_id':7, 'j_jeu':'Flight Simulator', 'j_description':"Simulation d'avion", 'j_sortie':1980, 'j_éditeur':'subLOGIC', 'j_genre':'Simulateur', 'j_id_support':1}, {'j_id':8, 'j_jeu':'DONKEY KONG', 'j_description':"Un méchant singe géant", 'j_sortie':1981, 'j_éditeur':'Nintendo', 'j_genre':'Plate-formes', 'j_id_support':None}, {'j_id':9, 'j_jeu':'PIFALL!', 'j_description':"Pitfall Harry, un explorateur", 'j_sortie':1982, 'j_éditeur':'Activision', 'j_genre':'Plate-formes', 'j_id_support':2}, {'j_id':10, 'j_jeu':'PIFALL!', 'j_description':"Pitfall Harry, un explorateur", 'j_sortie':1983, 'j_éditeur':'Nintendo', 'j_genre':'Plate-formes', 'j_id_support':3}, {'j_id':11, 'j_jeu':'Boulder Dash', 'j_description':"Rockford, mineur téméraire", 'j_sortie':1983, 'j_éditeur':'First Star Software', 'j_genre':'Plate-formes', 'j_id_support':3} ] console = [ {'c_id':1, 'c_nom':'Apple II', 'c_RAM':'4 ko', 'c_date_sortie':'1977-06-10'}, {'c_id':2, 'c_nom':'Atari 2600', 'c_RAM':'4 ko', 'c_date_sortie':'1977-10-14'}, {'c_id':3, 'c_nom':'C64', 'RAM':'c_64 ko', 'c_date_sortie':'1982-08-01'} ] print("Exemple de projection") nouvelle_table = projeter(jeu, ['j_jeu', 'j_description']) for nuplet in nouvelle_table: # pour chaque n-uplet de la jointure print(nuplet) # affiche ce n-uplet print("\n\nExemple de filtrage") nouvelle_table = uniquement_plateformes(jeu) for nuplet in nouvelle_table: # pour chaque n-uplet de la jointure print(nuplet) # affiche ce n-uplet

Comme vous pouvez le voir, le SQL est finalement un langage parfaitement adapté à sa tâche, bien plus simple à manipuler que Python pour faire ce job précis.

8 - FAQ

Il reste encore des choses à apprendre ?

Il existe un très grand nombre de possibilités en SQL. Le but de la découverte de SQL en NSI n'est pas de faire de vous des spécialistes du langage et des bases de données. A titre d'exemple, voici une sorte de requête comportant un très grand nombre de clauses :

1 2 3 4 5 6 7 8 9
SELECT * FROM table WHERE condition GROUP BY expression HAVING condition { UNION | INTERSECT | EXCEPT } ORDER BY expression LIMIT count OFFSET start;

On retiendra simplement la structure de base à base de

1 2 3 4
SELECT * FROM table WHERE condition(s) ORDER BY expression
  1. SELECT (PROJECTION) : on choisit les attributs qu'on veut réellement récupérer dans les n-uplets obtenus avec FROM WHERE. L'utilisation de l'étoile * veut dire de récupérer l'intégralité des attributs disponibles sur les nuplets obtenus.
  2. FROM : on indique la table dans laquelle on veut récupèrer des nuplets
  3. WHERE (FILTRAGE): on fournit les conditions à respecter pour qu'un nuplet soit sélectionné. On pourra utiliser AND, OR, LIKE...
  4. ORDER BY (TRI) : on fournit l'attribut de tri avec un suffixe ASC ou DESC qui permettra d'ordonner les n-uplets obtenus

Activité publiée le 18 01 2021
Dernière modification : 26 11 2023
Auteur : ows. h.