Procédures stockées (Procedures)
Les procédures stockées sont des blocs de code SQL qui peuvent être appelés et exécutés à partir d’autres parties de votre code SQL.
Elles permettent de regrouper des instructions SQL en un seul bloc logique et de les exécuter à partir de n’importe où dans votre code.
Les procédures stockées sont souvent utilisées pour effectuer des tâches répétitives, pour améliorer les performances et pour simplifier la maintenance du code.
Elles s’apparentent aux fonctions dans les langages de programmation.
Procedures et SQLite
Les procédures stockées sont cependant prises en charge par la plupart des autres moteurs de base de données, tels que MySQL, PostgreSQL, SQL Server, Oracle, etc.
Les exemples présentés ont été vérifié avec MySQL 9 et supérieur. Il faudra peut-être adapter les exemples pour d’autres moteurs de base de données.
Si vous voulez tester les exemples de procédures, vous pouvez installer MySQL sur votre machine ou utiliser un service en ligne comme db-fiddle.
Création d’une procédure
Comme pour les triggers, on change le délimiteur pour définir une procédure stockée.
1DELIMITER //2CREATE PROCEDURE nom_procedure( param1 TYPE, param2 TYPE)3BEGIN4 -- Instructions5END//6DELIMITER ;
Exemple
1DELIMITER //2CREATE PROCEDURE ajoute_100_aux_comptes()3BEGIN4 UPDATE Comptes5 SET solde = solde + 1006 WHERE id = 1;7END//8DELIMITER ;
Version commentée
1DELIMITER // -- On change le délimiteur2CREATE PROCEDURE ajoute_100_aux_comptes() -- Nom de la procédure3BEGIN -- Début du bloc4 UPDATE Comptes -- Instruction SQL5 SET solde = solde + 1006 WHERE id = 1;7END// -- Fin du bloc8DELIMITER ; -- On rétablit le délimiteur par défaut
Version transaction pour SQLite :
1BEGIN;2UPDATE Comptes3SET solde = solde + 1004WHERE id = 1;5COMMIT;
Paramètres
Les procédures stockées peuvent accepter des paramètres en entrée et retourner des valeurs en sortie.
1CREATE PROCEDURE nom_procedure( IN _param1 TYPE, OUT _param2 TYPE, INOUT _param3 TYPE)2BEGIN3 -- Instructions4END;
Exemple
1DELIMITER //2CREATE PROCEDURE maj_solde( IN _id_compte INT, IN _montant INT)3BEGIN4 UPDATE Comptes5 SET solde = solde + _montant6 WHERE id = _id_compte;7END//8DELIMITER ;
Paramètres de sortie
Les procédures stockées peuvent également retourner des valeurs en sortie.
Contrairement aux langages de programmation qui utilisent le mot-clé return
, les procédures stockées utilisent le mot-clé OUT
sur les paramètres de sortie.
Après execution de la procédure, les valeurs des paramètres de sortie peuvent être récupérées.
1CREATE PROCEDURE nom_procedure( INOUT _param1 TYPE, OUT _param2 TYPE)2BEGIN3 -- Instructions4END;
Exemple
1DELIMITER //2CREATE PROCEDURE solde_max( OUT _max_solde INT )3BEGIN4 SELECT MAX(solde) INTO _max_solde5 FROM Comptes;6END//7DELIMITER ;8
9CALL solde_max(@_max_solde); -- Appel de la procédure10
11SELECT @_max_solde; -- Affichage de la valeur de sortie
”Programmation” dans les procédures
Variables locales
Les procédures stockées peuvent déclarer des variables locales pour stocker des valeurs temporaires.
1CREATE PROCEDURE nom_procedure()2BEGIN3 DECLARE nom_variable TYPE;4 -- Instructions5END;
Exemple
1DROP PROCEDURE IF EXISTS maj_solde;2DELIMITER //3CREATE PROCEDURE maj_solde( IN _id_compte INT, IN _montant INT)4BEGIN5 DECLARE _solde_compte INT;6
7 SELECT solde INTO _solde_compte FROM Comptes WHERE id = _id_compte;8
9 UPDATE Comptes10 SET solde = _solde_compte + _montant11 WHERE id = _id_compte;12END//13DELIMITER ;
Programmation
Curseurs
Les curseurs sont utilisés pour parcourir les résultats d’une requête SQL.
Les curseurs sont souvent utilisés dans les procédures stockées pour traiter les résultats d’une requête ligne par ligne.
1DECLARE nom_curseur CURSOR FOR SELECT colonne1, colonne2 FROM table;2OPEN nom_curseur;3FETCH nom_curseur INTO variable1, variable2;4CLOSE nom_curseur;
Exemple
1DELIMITER //2CREATE PROCEDURE affiche_comptes()3BEGIN4 DECLARE id_compte INT;5 DECLARE solde_compte INT;6 DECLARE cur CURSOR FOR SELECT id, solde FROM Comptes;7 OPEN cur;8 FETCH cur INTO id_compte, solde_compte;9 WHILE @FETCH_STATUS = 0 DO10 SELECT id_compte, solde_compte;11 FETCH cur INTO id_compte, solde_compte;12 -- Ici on pourra executer des instructions qui utilisent les valeurs des colonnes13 END WHILE;14 CLOSE cur;15END//16DELIMITER ;17
18CALL affiche_comptes();
Programmation
Instruction CASE
Les instructions CASE
peuvent être utilisées dans les procédures stockées pour effectuer des opérations conditionnelles.
1CASE2 WHEN condition1 THEN instruction13 WHEN condition2 THEN instruction24 ELSE instruction35END CASE;
Exemple
1DROP PROCEDURE IF EXISTS maj_solde;2DELIMITER //3CREATE PROCEDURE maj_solde( IN _id_compte INT, IN _montant INT)4BEGIN5 DECLARE _solde_compte INT;6
7 SELECT solde INTO _solde_compte FROM Comptes WHERE id = _id_compte;8
9 UPDATE Comptes10 SET solde = CASE11 WHEN _solde_compte > 1000 THEN _solde_compte + _montant12 ELSE _solde_compte13 END14 WHERE id = _id_compte;15END//16DELIMITER ;
Programmation
Instruction IF
Les instructions IF
peuvent être utilisées dans les procédures stockées pour effectuer des opérations conditionnelles.
1IF condition THEN instruction1;2ELSE instruction2;3END IF;
Exemple
1DROP PROCEDURE IF EXISTS maj_solde;2DELIMITER //3CREATE PROCEDURE maj_solde( _id_compte INT, _montant INT)4BEGIN5 DECLARE _solde_compte INT;6
7 SELECT solde INTO _solde_compte FROM Comptes WHERE id = _id_compte;8
9 IF _solde_compte > 1000 THEN10 UPDATE Comptes11 SET solde = _solde_compte + _montant12 WHERE id = _id_compte;13 END IF;14END//15DELIMITER ;
Appel d’une procédure
1CALL nom_procedure();
Exemples
1CALL maj_solde(1, 100);
En utilisant un paramètre de sortie :
1CALL solde_max(@_max);2SELECT @_max;
Suppression d’une procédure
1DROP PROCEDURE IF EXISTS nom_procedure;
Procédures et Javascript
Certains SGBD comme MySQL supporte désormais le langage Javascript pour écrire des procédures stockées.
1CREATE PROCEDURE maj_solde( _id_compte INT, _montant INT)2LANGUAGE JAVASCRIPT AS $$3 let solde_compte = 0;4 let query = `SELECT solde FROM Comptes WHERE id = ${_id_compte}`;5 let result = mysql.query(query);6 if (result.length > 0) {7 solde_compte = result[0].solde;8 }9 let new_solde = solde_compte + _montant;10 query = `UPDATE Comptes SET solde = ${new_solde} WHERE id = ${_id_compte}`;11 mysql.query(query);12$$;
Procédures et langages de programmation
Appel de procédures dans une application
Voici quelques exemples d’appels de procédures stockées dans différentes langages de programmation.
1<?php2$pdo = new PDO('mysql:host=localhost;dbname;', 'user', 'password'); // Connexion à la base de données3$stmt = $pdo->prepare("CALL maj_solde(1, 100)"); // Préparation de la requête SQL4$stmt->execute(); // Execution de la procédure5?>
1import mysql.connector2mydb = mysql.connector.connect(host="localhost", user="user", password="password", database="db" )3mycursor = mydb.cursor()4mycursor.callproc('maj_solde', (1, 100))5mydb.commit()
1const mysql = require('mysql');2const connection = mysql.createConnection({ host: 'localhost', user: 'user', password: 'password', database: 'db' });3connection.connect();4connection.query('CALL maj_solde(1, 100)', function (error, results, fields) {5 if (error) throw error;6 console.log('Solde mis à jour');7});8connection.end();
1import java.sql.*;2public class Main {3 public static void main(String[] args) {4 try {5 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/db", "user", "password");6 CallableStatement cstmt = conn.prepareCall("{call maj_solde(?, ?)}");7 cstmt.setInt(1, 1);8 cstmt.setInt(2, 100);9 cstmt.execute();10 System.out.println("Solde mis à jour");11 } catch (SQLException e) {12 e.printStackTrace();13 }14 }15}
Procédure et SQLite
SQLite ne supporte pas les procédures stockées, il faudra définir les procédures dans le code de l’application.
1<?php2$GLOBALS["pdo"] = new PDO('sqlite:ma_base.db'); // Connexion à la base de données3
4function maj_solde($id_compte, $montant) {5 global $pdo;6 $stmt = $pdo->prepare("SELECT solde FROM Comptes WHERE id = ?");7 $stmt->execute([$id_compte]);8 $solde = $stmt->fetchColumn();9 $new_solde = $solde + $montant;10 $stmt = $pdo->prepare("UPDATE Comptes SET solde = ? WHERE id = ?");11 $stmt->execute([$new_solde, $id_compte]);12}13
14maj_solde(1, 100);15?>
1import sqlite32
3function maj_solde(id_compte, montant):4 conn = sqlite3.connect('ma_base.db')5 cur = conn.cursor()6 cur.execute("SELECT solde FROM Comptes WHERE id = ?", (id_compte,))7 solde = cur.fetchone()[0]8 new_solde = solde + montant9 cur.execute("UPDATE Comptes SET solde = ? WHERE id = ?", (new_solde, id_compte))10 conn.commit()11
12maj_solde(1, 100)
1const sqlite3 = require('sqlite3').verbose();2const db = new sqlite3.Database('ma_base.db');3
4function maj_solde(id_compte, montant) {5 db.serialize(() => {6 db.get("SELECT solde FROM Comptes WHERE id = ?", [id_compte], (err, row) => {7 if (err) {8 console.error(err.message);9 }10 let solde = row.solde;11 let new_solde = solde + montant;12 db.run("UPDATE Comptes SET solde = ? WHERE id = ?", [new_solde, id_compte], (err) => {13 if (err) {14 console.error(err.message);15 }16 console.log(`Solde mis à jour`);17 });18 });19 });20}21
22maj_solde(1, 100);
1import java.sql.*;2
3public class Main {4
5 public void maj_solde(int id_compte, int montant) {6 try {7 Connection conn = DriverManager.getConnection("jdbc:sqlite:ma_base.db");8 Statement stmt = conn.createStatement();9 ResultSet rs = stmt.executeQuery("SELECT solde FROM Comptes WHERE id = 1");10 int solde = rs.getInt("solde");11 int new_solde = solde + 100;12 stmt.executeUpdate("UPDATE Comptes SET solde = " + new_solde + " WHERE id = 1");13 System.out.println("Solde mis à jour");14 } catch (SQLException e) {15 e.printStackTrace();16 }17 }18
19 public static void main(String[] args) {20 Main main = new Main();21 main.maj_solde(1, 100);22 }23}
Conclusion
- Les procédures stockées permettent de regrouper des instructions SQL en un seul bloc logique
- Elles peuvent ensuite être appelées directement depuis le SGBD ou bien depuis une application
- Elles permettent de simplifier la maintenance du code, d’améliorer les performances et de réduire la duplication du code.
- Pour SQLite quqi ne supporte pas les procédures, il faudra définir les procédures dans le code de l’application
Exercice
Créez une procédure stockée bonus
qui :
- augmente le solde des comptes d’un pourcentage passé en paramètre, uniquement si le solde est supérieur au montant total de ses commandes
- qui renvoie les identifiants des comptes mis à jour
Solution
1CREATE PROCEDURE bonus( _pourcentage INT)2BEGIN3 DECLARE _id_compte INT;4 DECLARE _solde_compte INT;5 DECLARE _total_commandes INT;6 DECLARE _bonus INT;7 DECLARE _cur CURSOR FOR SELECT id, solde FROM Comptes;8 OPEN _cur;9 FETCH _cur INTO _id_compte, _solde_compte;10 WHILE @FETCH_STATUS = 0 DO11 SELECT SUM(montant) INTO _total_commandes FROM Commandes WHERE id_compte = _id_compte;12 IF _solde_compte > _total_commandes THEN13 SET _bonus = _solde_compte * _pourcentage / 100;14 UPDATE Comptes SET solde = solde + _bonus WHERE id = _id_compte;15 SELECT _id_compte;16 END IF;17 FETCH _cur INTO id_compte, _solde_compte;18 END WHILE;19 CLOSE _cur;20END;