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 title qui contient des informations sur les films, les séries et autres productions
  • Une relation person qui contient des informations sur les personnes ayant travaillé sur les productions précédentes
  • Une relation job qui contient des informations sur les emplois occupés et qui possède donc des clés secondaires menant à des n-uplets de title et person.

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
ORIGINE DES DONNEES

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 2024.

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.

Intro-04° Sélectionnez l'onglet Parcourir les données.

DB Brownser

Depuis cet onglet, vous avez la possibilité de parcourir les données un peu comme si vous étiez dans un tableur. C'est pratique, mais cela ne permet pas d'automatiser les choses.

Intro-05° Cliquez sur la case start_year de l'en-tête : vous devriez voir que les données sont triées en ordre croissant d'année de sortie.

Cliquez à nouveau sur la même case, vous devriez voir que les données sont triées en ordre décroissant d'année de sortie. Si vous voyez des productions du futur, c'est parce qu'elles sont déjà dans la base même si la date de sortie officielle n'est pas encore atteinte.

Intro-06° Placez dans la case sous primary_title (celle où apparaît mot filter) le nom d'un film que vous connaissez, son nom anglais. Si vous n'avez pas d'idée, tabler sur Die Hard, Star Wars ect...

DB Brownser

Comme vous le voyez, vous venez de récupérer les productions dont le nom contient le string que vous avez fourni.

Cliquer, c'est pratique pour une tâche mais cela ne permet pas d'automatiser quoi que ce soit.

Il est temps de passer à la programmation.

SQL

SQL veut dire Structured Query Language.

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.

2 - Projection : SELECT a FROM b

01° Dans le menu Structure de la base de données, cliquez sur la table title. Elle devrait apparaître en bleu. Cliquer alors sur le bouton modifier la table.

DB Brownser

Vous devriez pouvoir voir le code ayant permis de créer cette table :

DB Brownser
Pas de CREATE TABLE au BAC

Je donne ici quelques indications sur le SQL permettant de créer les tables car il est visible depuis DB Browser.

Vous n'avez pas à les connaître pour l'épreuve du BAC.

Déclaration d'un schéma relationnel en SQL

Voici les deux tables qui ont été créées pour cette activité :

1 2 3 4 5 6 7 8
CREATE TABLE person ( id INTEGER, name VARCHAR(50), birth_year INTEGER, death_year INTEGER, primary_profession TEXT, PRIMARY KEY(id AUTOINCREMENT) ;
1 2 3 4 5 6 7 8 9 10 11
CREATE TABLE title ( id INTEGER, 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(id AUTOINCREMENT) );
  • title_type : film de cinéma (movie), téléfilm (tvMovie), série (tvSeries), mini-série (tvMiniSeries)...
  • primary_title : le titre sous lequel cette œuvre est connue sur le site américain source de ces données.
  • original_title : le titre originel de l'œuvre
  • start_year et end_year : end_year est surtout utile pour les séries. Un film se termine normalement la même année que sa sortie...
  • runtime_minutes : la durée en minutes.
  • genres : un texte donnant le ou les genres de l'œuvre. Exemple : Action, Crime, Drama.

Clé primaire : PRIMARY KEY fait de id la clé primaire de cette table. Elle est donc automatiquement NOT NULL et UNIQUE. La présence de AUTOINCREMENT indique qu'on n'a pas besoin de fournir la valeur de clé lorsqu'on insère un nouvel enregistrement : c'est le SGBD qui s'en charge.

VARCHAR : VARCHAR(40) veut dire qu'on crée un attribut de type texte qui pourra prendre jusqu'à 40 caractères. CHAR(40) demande d'avoir exactement 40 caractères. SQLite transformera le tout en TEXT à l'interne...

02° Nous allons maintenant voir comment demander des informations à la base de données en SQL.

  1. Allez dans EXECUTER LE SQL
  2. DB Brownser
  3. Tapez la requête SQL suivante :
  4. 1 2
    SELECT primary_title, runtime_minutes, genres FROM title;
  5. Exécutez la requête en appuyant sur le bouton FLECHE.
  6. DB Brownser

Comme vous le voyez, vous venez de projeter certaines informations sur les productions : nom, durée et genres.

2.1 PROJECTION avec SELECT attributs FROM table

Choix d'attributs

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

1 2
SELECT primary_title, runtime_minutes, genres FROM title;

TRADUCTION

Projette le titre, la durée et le genre
des n-uplets de la table title.

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 et à former plusieurs 3-uplets, un pour chaque ligne.

Table titre avec nom, durée et genres sélectionnés
Sélection du nom, de la durée et des genres

On obtient alors ceci :

DB Brownser
Ordre des attributs dans SELECT

L'ordre des attributs projetés avec SELECT est respecté dans la réponse fournie. Si on inverse le genre et la durée, on obtient bien ces attributs dans cet ordre ;

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

03° Fournir la requête SQL permettant de projeter, pour tous les n-uplets de la table title :

  • le nom,
  • la clé primaire (le numéro d'enregistrement)
  • et la date de création

Voici le schéma relationnel de la table :

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

...CORRECTION...

SELECT primary_title, id, start_year FROM title

04° Fournir la requête SQL permettant de projeter le nom, la date de naissance et la date de mort éventuelle pour tous les n-uplets de la table person.

Voici le schéma relationnel de la table :

1 2 3 4 5 6 7
person ( id INTEGER, name VARCHAR(50), birth_year INTEGER, death_year INTEGER, primary_profession TEXT )

...CORRECTION...

SELECT name, birth_year, death_year
FROM person
2.2 Clause DISTINCT

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

1 2
SELECT DISTINCT title_type FROM title;

TRADUCTION

Projette un unique exemplaire de chaque type de production présent
des n-uplets de la table title.

0SELECT DISTINCT title_type
FROM title

05-A° Fournir la requête SQL permettant de projeter de façon unique les durées des productions présentes dans la table title.

Voici le schéma relationnel de la table :

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

...CORRECTION...

SELECT DISTINCT runtime_minutes
FROM title;

Comme vous le voyez, des résultats sont fournis mais difficile d'en tirer des informations pertinentes car elles ne sont pas triées.

05-B° Utiliser cette dernière requête qui va nous permettre de trier les données avant de les projeter :

1 2 3
SELECT DISTINCT runtime_minutes FROM title ORDER BY runtime_minutes;

Cette fois, vous devriez voir que les données sont triées en ordre croissant.

3 - Filtrage : ... WHERE c

Vous savez projeter certains attributs ("colonnes") avec SELECT.
Notez que projeter contient un o comme colonne.

Vous allez maintenant voir comment filtrer les n-uplets ("lignes") avec WHERE.
Notez que filtrer contient un i comme ligne.

Pour cela, il faut fournir dans cet ordre :

  1. Les attributs qu'on veut récupérer (SELECT)
  2. La table ciblée (FROM)
  3. La condition sur le filtrage (WHERE)
3.1 FILTRAGE avec la clause WHERE condition

Quelques opérateurs booléens

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

Cette clause utilise des opérateurs comme :

  • Opérateur d'égalité :   = (équivalent SQL du == en Python)
  • Opérateur de différence :   <> (équivalent du != en Python)
  • Les opérateurs de comparaisons :   <, <=, >, >=
  • Les opérateurs logiques :   AND, OR, NOT
  • Les opérateurs arithmétiques :   +, -, *, /, %
1er exemple (basique)

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

1 2 3
SELECT primary_title, start_year, runtime_minutes FROM title WHERE start_year = 2024;

TRADUCTION

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

SELECT primary_title, start_year, runtime_minutes FROM title WHERE start_year = 2024;
2e exemple (plusieurs conditions)

On veut récupérer les titres qui ont commencé en 2024 et dont la durée est comprise entre 60 min et 80 min.

1 2 3
SELECT primary_title, start_year, runtime_minutes FROM title WHERE start_year = 2024 AND runtime_minutes >= 60 AND runtime_minutes <= 80;

TRADUCTION

Projette le titre, l'année de sortie et la durée
des n-uplets de title
qui sont sortis en 2024 et qui durent entre 60 et 80 mn.

Le résultat :

x

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

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

...CORRECTION...

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

Le résultat visuel :

x
Le piège du ET

N'oubliez pas qu'il faut placer une condition booléenne devant et derrière votre ET.

Un humain comprend : si la date est supérieure à 2010 ET inférieure à 2012. Vous parvenez à donner du contexte à la partie rouge pour voir qu'elle se rattache à l'une information date fournie dans la partie bleue.

Un systéme informatique ne peut pas faire cela, il a besoin de deux vraies conditions indépendantes de chaque côté : si la date est supérieure à 2010 ET si la date est inférieure à 2012.

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

Rappel des catégories des titres :

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

...CORRECTION...

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

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

x

08° Fournir la requête permettant d'obtenir les séries terminées ayant eu une durée d'au moins 10 ans.

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

...CORRECTION...

1 2 3 4 5
SELECT primary_title, start_year, end_year FROM title WHERE title_type = "tvSeries" AND (end_year - start_year) >= 10;
x
3.2 Filtrage et NULL

Le cas des informations NULL

NULL signifie soit ABSENCE D'INFORMATIONS, soit INFORMATION NON PERTINENTE, soit INCERTITUDE. On ne peut donc avoir aucune certitude sur les filtrages qui en dépendent.

Conséquence : tout n-uplet ayant un attribut nécessaire au filtrage mais de valeur NULL sera écarté.

Un exemple simple

On veut récupérer les titres dont la date de fin est inférieure à 2024.

1 2 3
SELECT primary_title, end_year, runtime_minutes FROM title WHERE end_year < 2024;

On a besoin d'une date de fin, aucun n-uplet comportant une valeur NULL pour la date de fin ne va donc apparaître dans la réponse.

Exemple complémentaire du précédent

On veut récupérer les titres dont la date de fin n'est pas inférieure à 2024.

1 2 3
SELECT primary_title, end_year, runtime_minutes FROM title WHERE NOT (end_year < 2024);

Encore une fois, seuls les n-uplets ayant une vraie valeur sont renvoyés.

Notez que cette formulation est équivalente :

1 2 3
SELECT primary_title, end_year, runtime_minutes FROM title WHERE end_year >= 2024;
Le cas plus subtil : l'opérateur d'égalité

Imaginons qu'on cherche à obtenir les productions qui n'ont pas de date de fin. On pourrait penser qu'un tel code pourrait fonctionner :

1 2 3
SELECT primary_title, end_year, runtime_minutes FROM title WHERE end_year = NULL;

Mais non : puisque l'attribut end_year apparaît, la base de données écartera de base tous les n-uplets dont cet attribut est NULL.

Récupérer les NULL : opérateur d'identité

Si vous voulez localiser les attributs NULL par filtrage, il faut utiliser la syntaxe prévue à cet effet : l'opérateur IS.

1 2 3
SELECT primary_title, end_year, runtime_minutes FROM title WHERE end_year IS NULL;

Cette fois cela fonctionne : on récupère bien les nuplets n'ayant pas d'année de fin.

Le résultat :

x

09-A° Fournir la requête permettant de projeter le nom et la date de naissance des personnes encore en vie présentes dans la base de données.

1 2 3 4 5 6 7
person ( id INTEGER, name VARCHAR(50), birth_year INTEGER, death_year INTEGER, primary_profession TEXT )

...CORRECTION...

x

09-B° En analysant la projection fournie par la base de données, vous devriez voir qu'on récupère pas mal de personnes dont on ne connaît pas non plus la date de naissance : ces n-uplets apparaissent néanmoins car l'attribut birth_year ne faisait pas partie du filtrage.

Modifiez la requête précédente pour ne plus récupérer les n-uplets dont la date de naissance est NULL.

...CORRECTION...

x
3.3 Rappel : priorités du AND sur le OR

Dans l'algèbre de Boole et en travaillant sur le domaine {0, 1} :

  • on peut écrire a AND b comme a.b.
  • on peut écrire a OR b comme a+b.

En conclusion :

Le NOT est prioritaire sur AND et OR.

Le AND est prioritaire sur OR.

10° Voici deux requêtes.

Question : les deux requêtes sont-elles similaires ?

Pour répondre à la question, il vous suffit de rajouter les parenthèses telles que les placeraient l'interpréteur SQL.

Requête 1

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

Requête 2

1 2 3
SELECT primary_title, start_year, end_year FROM title 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 évaluer les AND et ensuite on utilise le OU.

4 - Commande ORDER BY

On donne ici quelques précisions sur le tri des données de la projection fournie.

4.1 Trier les résultats avec ORDER BY

Principe

La commande ORDER BY permet de trier les n-uplets (lignes) d’une projection. 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 title 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 title WHERE title_type = "movie" ORDER BY start_year ASC;
4.2 Trier les résultats avec ORDER BY en ordre décroissant

Tri en ordre décroissant avec

On précise alors le suffixe DESC.

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

11° Projeter le nom et la date de naissance des personnes nées entre 1970 et 1980 en les triant par ordre alphabétique.

1 2 3 4 5 6 7
person ( id INTEGER, name VARCHAR(50), birth_year INTEGER, death_year INTEGER, primary_profession TEXT )

...CORRECTION...

x

12° Projeter le nom et la durée des films sortis en 2024 (et uniquement les films) en les triant par durée, du plus long au moins long (DESC).

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

...CORRECTION...

x
4.3 Tri sur plusieurs colonnes avec ORDER BY (Hors programme ?)

Tri multiple

Lors d'un tri, en cas de l'égalité entre plusieurs n-uplets, la base propose simplement les n-uplets par ordre d'apparition dans la base.

Après l'attribut de tri principal, on peut néanmoins placer une virgule et un deuxième attribut qui servira à trier entre eux les n-uplets à égalité lors du tri principal.

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

On trie les fims pour date de sortie décroissante DESC (les plus récentes d'abord). En cas d'égalité, on les trie selon leurs noms (alphabétique, par ordre croissant ASC).

x

5 - Fonctions d'agrégation

On peut également projeter des calculs sur les données.

5.1 MAX()

Recherche de maximum

Pour trouver la plus grande durée parmi les films :

1 2 3
SELECT MAX(runtime_minutes) FROM title WHERE title_type = 'movie';

TRADUCTION

Projette la plus grande durée
des n-uplets de title
qui sont des films.

x
Projeter autre chose en même temps : un seul cas apparaît, pas tous

Attention à ce type de code : il ne fonctionne pas comme vous pourriez le penser.

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

En réalité, la base de données fournit le premier enregistrement dont la durée est le maximum. Si c'est le seul, la réponse est exact. Si il y en a plusieurs, ce n'est que l'un des cas possibles. Sur le cas de la durée, cet énorme documentaire sur la logistique est bien le premier et l'unique ayant cette durée !

Mais si on cherche les acteurs dont la date de naissance est la plus grande, on ne peut pas être certain que celui qui apparaît est le seul et l'unique : x

5.2 MIN()

Recherche de minimum

Pour trouver la durée de film la plus courte et l'un des n-uplets correspondants :

1 2 3
SELECT primary_title, MIN(runtime_minutes) FROM title 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 title
qui sont des films.

5.3 AVG()

Calcul de moyenne

Pour trouver la durée moyenne des films :

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

TRADUCTION

Projette la durée moyenne calculée
à partir des n-uplets de title
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 title WHERE title_type = 'movie' and start_year = 2020;

TRADUCTION

Projette la somme des durées
à partir des n-uplets de title
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é

N'oubliez pas que si vous demandez en même temps d'afficher un attribut, vous n'aurez qu'un seul résultat : le premier qui correspondra dans la base.

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

  • 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.

Pour les plus rapides, nous verrons comment grouper les deux requêtes pour n'en faire qu'une seule mais ce n'est pas au programme.

13° En réalisant plusieurs requêtes, fournir la somme des durées de films sortis en 2020, 2021, 2022, 2023 et 2024.

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

...CORRECTION...

2020 :

x

2021 :

x

2022 :

x

2023 :

x

2024 :

x

14° 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
title ( id INTEGER, title_type VARCHAR(15), primary_title VARCHAR(40), original_title VARCHAR(40), start_year INTEGER, end_year INTEGER, runtime_minutes INTEGER, genres TEXT )

...CORRECTION...

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

On aurait alors juste un affichage de ce type :

x
5.5 Obtenir le nombre d'occurrences 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 title WHERE start_year = 2000;

TRADUCTION

Projette l'année de sortie voulue et le nombre de n-uplets récupérés
à partir des n-uplets de title
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.

15° Projeter le nombre de titres de plus de 180 minutes.

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

...CORRECTION...

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

On aurait alors juste un affichage de ce type :

x

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)

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

6.1 Ressemblance avec LIKE (à savoir utiliser si rappel fourni)

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 title WHERE primary_title LIKE '%alien %' OR '%aliens%';

TRADUCTION

Projette toutes les informations
des n-uplets de la table title
dont le titre contient le texte "alien " ou le texte "aliens".

x

16° Projeter les noms des films dont le genre contient "Sci-Fi".

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

...CORRECTION...

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

17° Projeter toutes les informations disponibles sur les films de Fantasy, en les triant du plus récent au plus ancien et dans l'ordre alphabétique en cas d'égalité.

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

...CORRECTION...

1 2 3 4
SELECT * FROM title WHERE genres LIKE '%Fantasy%' and title_type = 'movie' 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 title WHERE title_type = "tvSeries" OR title_type = "movie";

Version avec un IN

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

TRADUCTION dans les deux cas

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

18° Projeter le titre et le type des films ou des séries datant de 2024. Contrainte : utilisez IN.

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

...CORRECTION...

x

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.

19° Exercice de compréhension de code : lancer le programme fourni dans la partie 7 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['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 = [ {'id':7, 'jeu':'Flight Simulator', 'description':"Simulation d'avion", 'sortie':1980, 'éditeur':'subLOGIC', 'genre':'Simulateur', 'id_support':1}, {'id':8, 'jeu':'DONKEY KONG', 'description':"Un méchant singe géant", 'sortie':1981, 'éditeur':'Nintendo', 'genre':'Plate-formes', 'id_support':None}, {'id':9, 'jeu':'PIFALL!', 'description':"Pitfall Harry, un explorateur", 'sortie':1982, 'éditeur':'Activision', 'genre':'Plate-formes', 'id_support':2}, {'id':10, 'jeu':'PIFALL!', 'description':"Pitfall Harry, un explorateur", 'sortie':1983, 'éditeur':'Nintendo', 'genre':'Plate-formes', 'id_support':3}, {'id':11, 'jeu':'Boulder Dash', 'description':"Rockford, mineur téméraire", 'sortie':1983, 'éditeur':'First Star Software', 'genre':'Plate-formes', '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, ['jeu', '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

20° 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 = [ {'id':7, 'jeu':'Flight Simulator', 'description':"Simulation d'avion", 'sortie':1980, 'éditeur':'subLOGIC', 'genre':'Simulateur', 'id_support':1}, {'id':8, 'jeu':'DONKEY KONG', 'description':"Un méchant singe géant", 'sortie':1981, 'éditeur':'Nintendo', 'genre':'Plate-formes', 'id_support':None}, {'id':9, 'jeu':'PIFALL!', 'description':"Pitfall Harry, un explorateur", 'sortie':1982, 'éditeur':'Activision', 'genre':'Plate-formes', 'id_support':2}, {'id':10, 'jeu':'PIFALL!', 'description':"Pitfall Harry, un explorateur", 'sortie':1983, 'éditeur':'Nintendo', 'genre':'Plate-formes', 'id_support':3}, {'id':11, 'jeu':'Boulder Dash', 'description':"Rockford, mineur téméraire", 'sortie':1983, 'éditeur':'First Star Software', 'genre':'Plate-formes', '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, ['jeu', '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 - Pour aller un peu plus loin

Pour aller plus loin : GROUP BY (hors programme)

En début de séance, nous cherchions les différents types de productions présents dans la table des titres.

Si nous voulons ensuite obtenir le nombre de productions par type, il faudrait faire une recherche par type et lancer une fonction d'agrégation COUNT().

1 2 3
SELECT title_type, COUNT(*) FROM title GROUP BY title_type;
x

Et de cette façon, on pourrait également connaitre le nombre de productions produites entre 2014 et 2024, classement par années :

1 2 3 4
SELECT start_year, COUNT(*) FROM title WHERE start_year >= 2014 AND start_year <= 2024 GROUP BY start_year;
x
Pour aller plus loin : SELECT de SELECT (hors programme)

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

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

1 2 3 4 5 6 7
SELECT primary_title, runtime_minutes, start_year FROM title WHERE runtime_minutes = ( SELECT MIN(runtime_minutes) FROM title 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 title WHERE runtime_minutes = (SELECT MIN(runtime_minutes) FROM title WHERE title_type = 'movie');
Pour aller plus loin : WITH (hors programme)

La clause WITH permet de structurer les requêtes en stockant des résultats intermédiaires dans des alias plutôt que de faire des SELECT de SELECT.

1 2 3 4 5
WITH duree AS (SELECT MIN(runtime_minutes) FROM title WHERE title_type = 'movie') SELECT primary_title, runtime_minutes, start_year FROM title WHERE runtime_minutes = duree

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.