Les curseurs dans MySQL
Sommaire
- 1- Objectifs
- 2- Introduction aux curseurs :
- 2.1- Définition
- 2.2- Comparaison avec les opérations ensemblistes.
- 2.3- Types d’un curseur
- 2.4- Propriétés des curseurs MySQL
- 2.4.1- Non-Scrollable
- 2.4.2- Lecture Seule
- 2.4.3- Asensible
- 3- Curseurs Explicites
- 3.1- Déclaration d'un Curseur Explicite en MySQL
- 3.2- Ouverture d'un curseur
- 3.3- Lecture des données avec un curseur
- 3.4- Boucler à travers les résultats
- 3.4.1- Boucle WHILE:
- 3.4.2- Boucle FOR:
- 3.4.3- Boucle REPEAT:
- 3.5- Fermeture d'un curseur
- 4- Exemple complet
- 5- Application
- 6- Utilisation des curseurs
- 6.1.1- Cours MySQL
Les curseurs dans MySQL
-
Objectifs
- À la fin de ce cours, vous serez capables de :
- Comprendre le concept et l’utilité des curseurs en SQL :
- Déclarer et utiliser des curseurs dans MySQL :
- Gérer le cycle de vie d’un curseur :
- Implémenter des traitements conditionnels et complexes avec des curseurs :
- Éviter et gérer les erreurs courantes liées à l’utilisation des curseurs :
-
Introduction aux curseurs :
-
Définition
- En MySQL, un curseur (ou « cursor » en anglais) est un outil utilisé pour parcourir ligne par ligne les résultats d’une requête SELECT. Il permet d’effectuer des opérations sur chaque ligne de manière individuelle, ce qui est particulièrement utile lorsque vous devez effectuer des traitements complexes qui ne peuvent pas être réalisés directement avec une seule requête SQL.
-
Comparaison avec les opérations ensemblistes.
- Les opérations ensemblistes consistent à manipuler des ensembles de données en une seule opération SQL. Elles sont généralement utilisées pour des requêtes SQL standard telles que SELECT, INSERT, UPDATE, DELETE avec des clauses comme WHERE, GROUP BY, HAVING, et ORDER BY.
-
Types d’un curseur
- En SQL, y compris en MySQL, les curseurs peuvent être classifiés en deux types principaux : les curseurs explicites et les curseurs implicites.
-
Propriétés des curseurs MySQL
- Les curseurs MySQL ont certaines propriétés spécifiques qui influencent leur utilisation dans les procédures stockées. Voici un résumé détaillé des propriétés des curseurs MySQL :
-
Non-Scrollable
- Description : Les curseurs MySQL sont non-scrollable, ce qui signifie que vous ne pouvez parcourir les lignes que dans une seule direction, de la première à la dernière.
- Implications : Pas de saut de ligne : Vous ne pouvez pas sauter une ligne spécifique et passer directement à une autre ligne.
- Pas de retour en arrière : Une fois que vous avez lu une ligne, vous ne pouvez pas revenir en arrière pour la relire.
-
Lecture Seule
- Description : Les curseurs MySQL sont en lecture seule, ce qui signifie que vous ne pouvez pas utiliser les curseurs pour mettre à jour ou supprimer des lignes dans la base de données.
- Implications : Aucune modification : Les curseurs servent uniquement à lire les données. Toute modification doit être effectuée en utilisant des requêtes SQL séparées.
-
Asensible
- Description : Les curseurs MySQL sont asensibles, ce qui signifie qu’ils pointent vers les données sous-jacentes en temps réel plutôt qu’un instantané des données.
- Performance : Les curseurs asensibles sont généralement plus rapides que les curseurs insensibles car ils accèdent directement aux données actuelles.
- Données Actuelles : Les modifications aux données sous-jacentes après l’ouverture du curseur seront visibles lorsque vous parcourez le curseur.
-
Curseurs Explicites
- Les curseurs explicites sont créés et gérés directement par les utilisateurs (développeurs ou administrateurs de base de données).
- Ils sont utilisés pour parcourir et traiter un ensemble de résultats ligne par ligne, généralement issus d’une requête SELECT qui retourne plusieurs lignes.
- L’utilisation d’un curseur explicite en MySQL suit quatre étapes principales : déclaration, ouverture, traitement des lignes et fermeture.
-
Déclaration d’un Curseur Explicite en MySQL
- Pour utiliser un curseur, vous devez d’abord le déclarer dans un bloc de code de type procédure stockée, fonction ou déclencheur.
- Pour déclarer un curseur, il faut utiliser l’instruction
DECLARE
CURSOR
avec leSELECT
permettant d’avoir le jeu de données dans la zone de déclaration de la procédure (donc après le BEGIN et avant les HANDLERS). - La déclaration d’un curseur spécifie la requête SQL dont les résultats seront parcourus.
- Pour créer un curseur MySQL, vous devrez travailler avec les instructions
DECLARE
,OPEN
,FETCH
etCLOSE
. -
Ouverture d’un curseur
- Après avoir déclaré le curseur, vous devez l’ouvrir pour initialiser son positionnement et préparer le jeu de résultats à être parcouru.
-
Lecture des données avec un curseur
- Vous utilisez la commande FETCH pour lire les lignes du curseur une par une. Chaque ligne lue peut être stockée dans des variables locales pour un traitement ultérieur.
-
Boucler à travers les résultats
- Souvent, vous parcourez les résultats d’un curseur à l’aide d’une boucle. Vous devez aussi vérifier la fin des données pour éviter des erreurs.
- Pour boucler sur les résultats d’un curseur, il va falloir ouvrir le curseur et la boucle, affecter les champs de la ligne à des variables, faire les traitements que vous souhaitez, et enfin fermer la boucle et le curseur :
- Il existe plusieurs façons de boucler sur les résultats d’un curseur MySQL dans un environnement MySQL.
-
Boucle WHILE:
-
Boucle FOR:
-
Boucle REPEAT:
-
Fermeture d’un curseur
- Une fois que vous avez terminé de travailler avec un curseur, il est important de le fermer pour libérer les ressources associées.
-
Exemple complet
- table tutorials suivante :
- On va créer la table tutorials avec des colonnes pour ID, TITLE, AUTHOR, et DATE.
- ID est défini comme clé primaire.
- Nous allons créer une autre table backup pour sauvegarder les données de la table tutorials :
- Les colonnes ont les mêmes types que celles de tutorials, mais ID n’est pas clé primaire.
- Nous allons créer une procédure stockée qui sauvegarde le contenu de la table tutorials dans la table backup à l’aide d’un curseur :
- Dans cet exemple, la procédure exemple_curseur utilise un curseur pour sélectionner des lignes d’une table et les traiter ligne par ligne. Les valeurs de chaque ligne sont simplement affichées, mais vous pouvez remplacer cette opération par n’importe quel traitement nécessaire.
-
Application
- Nous allons créer une table employees avec des informations sur les employés, cette table sera créée avec des colonnes pour ID, NAME, et SALARY et ID qui est défini comme clé primaire.
- Cinq lignes seront insérées dans la table avec des valeurs pour chaque colonne.
- Ensuite, nous créerons une procédure stockée qui utilise un curseur pour augmenter le salaire de chaque employé d’un certain pourcentage si certaines conditions sont remplies (par exemple, les employés dont le salaire est inférieur à une certaine valeur).
-
Utilisation des curseurs
DECLARE nom_du_curseur CURSOR FOR
SELECT colonne1, colonne2, ...
FROM table
WHERE condition;
OPEN nom_du_curseur;
FETCH nom_du_curseur INTO var1, var2, ...;
Utilisez
WHILE
,REPEAT
ouLOOP
pour itérer sur les résultats, en utilisant FETCH cur INTO … pour récupérer chaque ligne.
DECLARE user_cursor CURSOR FOR
SELECT * FROM users;
DECLARE @id INT, @name VARCHAR(50);
OPEN user_cursor;
FETCH NEXT FROM user_cursor INTO @id, @name;
WHILE @@FETCH_STATUS = 0
BEGIN
-- Faire quelque chose avec les résultats
PRINT 'ID: ' + CAST(@id AS VARCHAR(10)) + ', Name: ' + @name;
FETCH NEXT FROM user_cursor INTO @id, @name;
END
CLOSE user_cursor;
DEALLOCATE user_cursor;
DECLARE user_cursor CURSOR FOR
SELECT * FROM users;
DECLARE @id INT, @name VARCHAR(50);
OPEN user_cursor;
FETCH NEXT FROM user_cursor INTO @id, @name;
WHILE @@FETCH_STATUS = 0
BEGIN
-- Faire quelque chose avec les résultats
PRINT 'ID: ' + CAST(@id AS VARCHAR(10)) + ', Name: ' + @name;
FETCH NEXT FROM user_cursor INTO @id, @name;
END
CLOSE user_cursor;
DEALLOCATE user_cursor;
DECLARE user_cursor CURSOR FOR
SELECT * FROM users;
DECLARE @id INT, @name VARCHAR(50);
OPEN user_cursor;
FETCH NEXT FROM user_cursor INTO @id, @name;
WHILE @@FETCH_STATUS = 0
BEGIN
-- Faire quelque chose avec les résultats
PRINT 'ID: ' + CAST(@id AS VARCHAR(10)) + ', Name: ' + @name;
FETCH NEXT FROM user_cursor INTO @id, @name;
END
CLOSE user_cursor;
DEALLOCATE user_cursor;
CLOSE nom_du_curseur;
-- Crée la table tutorials
CREATE TABLE tutorials (
ID INT PRIMARY KEY, -- Identifiant unique pour chaque tutoriel
TITLE VARCHAR(100), -- Titre du tutoriel
AUTHOR VARCHAR(40), -- Auteur du tutoriel
DATE VARCHAR(40) -- Date de publication du tutoriel
);
-- Insère des données dans la table tutorials
INSERT INTO tutorials VALUES(1, 'Java', 'ALAMI', '2020-09-01');
INSERT INTO tutorials VALUES(2, 'Python', 'RAJI', '2020-05-01');
INSERT INTO tutorials VALUES(3, 'Java', 'BEN ALI', '2021-05-01');
INSERT INTO tutorials VALUES(4, 'Android', 'NAJI', '2022-02-01');
INSERT INTO tutorials VALUES(5, 'Php', 'BEN ALI', '2019-04-06');
-- Sélectionne et affiche toutes les lignes de la table tutorials pour vérification
SELECT * FROM tutorials;
-- Crée la table backup pour sauvegarder les données de la table tutorials
CREATE TABLE backup (
ID INT, -- Identifiant unique pour chaque tutoriel (pas nécessairement clé primaire ici)
TITLE VARCHAR(100), -- Titre du tutoriel
AUTHOR VARCHAR(100), -- Auteur du tutoriel
DATE VARCHAR(100) -- Date de publication du tutoriel
);
-- Change le délimiteur pour pouvoir utiliser des procédures stockées
DELIMITER //
-- Crée la procédure stockée ExampleProc1
CREATE PROCEDURE ExampleProc1()
BEGIN
DECLARE done INT DEFAULT 0; -- Indicateur de fin de curseur
DECLARE tutoID INT; -- Variable pour stocker l'ID du tutoriel
DECLARE tutoTitle, tutoAuthor, tutoDate VARCHAR(100); -- Variables pour stocker les détails du tutoriel
-- Déclare le curseur pour sélectionner toutes les colonnes de la table tutorials
DECLARE cur CURSOR FOR SELECT * FROM tutorials;
-- Gestionnaire d'événements pour la fin des résultats du curseur
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=1;
-- Ouvre le curseur
OPEN cur;
-- Boucle pour parcourir les résultats du curseur
label : LOOP
FETCH cur INTO tutoID, tutoTitle, tutoAuthor, tutoDate; -- Récupère une ligne du curseur
IF done=1 THEN -- Vérifie si c'est la fin du curseur
LEAVE label; -- Sort de la boucle si c'est la fin
END IF;
-- Insère les données récupérées dans la table backup
INSERT INTO backup VALUES(tutoID, tutoTitle, tutoAuthor, tutoDate);
END LOOP;
-- Ferme le curseur après la fin de la boucle
CLOSE cur;
END //
-- Change le délimiteur de retour à ;
DELIMITER ;
-- Appelle la procédure stockée ExampleProc1 pour exécuter la sauvegarde
CALL ExampleProc1();
-- Sélectionne et affiche toutes les lignes de la table backup pour vérification
SELECT * FROM backup;
-- Crée la table employees
CREATE TABLE employees (
ID INT PRIMARY KEY, -- Identifiant unique pour chaque employé
NAME VARCHAR(100), -- Nom de l'employé
SALARY DECIMAL(10,2) -- Salaire de l'employé
);
-- Insère des données dans la table employees
INSERT INTO employees VALUES(1, 'Alice', 30000.00);
INSERT INTO employees VALUES(2, 'Bob', 40000.00);
INSERT INTO employees VALUES(3, 'Charlie', 25000.00);
INSERT INTO employees VALUES(4, 'David', 45000.00);
INSERT INTO employees VALUES(5, 'Eve', 32000.00);
-- Sélectionne et affiche toutes les lignes de la table employees pour vérification
SELECT * FROM employees;
-- Change le délimiteur pour pouvoir utiliser des procédures stockées
DELIMITER //
-- Crée la procédure stockée IncreaseSalaries
CREATE PROCEDURE IncreaseSalaries()
BEGIN
DECLARE done INT DEFAULT 0; -- Indicateur de fin de curseur
DECLARE empID INT; -- Variable pour stocker l'ID de l'employé
DECLARE empName VARCHAR(100); -- Variable pour stocker le nom de l'employé
DECLARE empSalary DECIMAL(10,2); -- Variable pour stocker le salaire de l'employé
-- Déclare le curseur pour sélectionner toutes les colonnes de la table employees
DECLARE cur CURSOR FOR
SELECT ID, NAME, SALARY FROM employees;
-- Gestionnaire d'événements pour la fin des résultats du curseur
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done=1;
-- Ouvre le curseur
OPEN cur;
-- Boucle pour parcourir les résultats du curseur
read_loop: LOOP
FETCH cur INTO empID, empName, empSalary; -- Récupère une ligne du curseur
IF done THEN -- Vérifie si c'est la fin du curseur
LEAVE read_loop; -- Sort de la boucle si c'est la fin
END IF;
-- Condition pour augmenter le salaire
IF empSalary < 35000 THEN
-- Augmente le salaire de 10%
UPDATE employees
SET SALARY = empSalary * 1.10
WHERE ID = empID;
END IF;
END LOOP;
-- Ferme le curseur après la fin de la boucle
CLOSE cur;
END //
-- Change le délimiteur de retour à ;
DELIMITER ;
-- Appelle la procédure stockée IncreaseSalaries pour exécuter les mises à jour de salaire
CALL IncreaseSalaries();
-- Sélectionne et affiche toutes les lignes de la table employees pour vérifier les mises à jour
SELECT * FROM employees;