đŸ—ƒïž
SGBDR
  • SGBDR
  • Introduction
    • Introduction
    • Abstractions, DDL, DML et SQL
  • Setup initial
    • Options d'architecture
    • MariaDB via Docker (en dev)
    • Connexion
    • Import des donnĂ©es
    • SĂ©curisation et privilĂšges
  • Interrogation
    • Vocabulaire
    • La base "SaaS"
    • select
    • OpĂ©rations de set
    • AgrĂ©gation
    • Sous-requĂȘtes
    • Jointures
    • Pagination
  • Projet
    • Projet 1 : Pagination
  • Data modeling
    • Introduction
    • Design conceptuel
    • Design logique
    • DĂ©pendances fonctionnelles
    • Normalisation
  • Data dĂ©finition (DDL)
    • Introduction
    • Create table
    • Alter table
    • Identifiants
    • Types complexes
    • Exercice
  • Data manipulation (DML)
    • Update et delete
    • Transactions
    • Stored procedures
  • OpĂ©rations
    • Docker en opĂ©ration
    • Optimisation
    • Sauvegardes
  • Conclusion
    • Conclusion
  • Copyright Kevin Glass 2023
Propulsé par GitBook
Sur cette page
  • Stored procedures
  • Un dĂ©limiteur temporaire
  • Commande pour la crĂ©ation d'une procĂ©dure
  • Gestion d'erreurs
  • Invoquer une procĂ©dure stockĂ©e
  • Lister, modifier une procĂ©dure
  • Exercice
  1. Data manipulation (DML)

Stored procedures

PrécédentTransactionsSuivantDocker en opération

DerniĂšre mise Ă  jour il y a 1 an

Stored procedures

Nous pouvons pré-coder des fonctions directement dans la base de données, qui se compose de plusieurs commandes SQL, ainsi que des transactions.

Quels avantages ?

  • La logique business est codĂ©e Ă  un point central de la plateforme

  • Moins d’erreurs

  • Maintenance plus facile

  • EmpĂȘcher de l’accĂšs direct aux tables par les utilisateurs et/ou APIS

  • Abstraire de la complexitĂ©

  • ContrĂŽle d’accĂšs et permissions

Imaginons le transfert d’argent dans une plateforme bancaire. La sĂ©quence de requĂȘtes se trouve oĂč ?

On peut, bien sûr, implémenter la logique dans le client :

En revanche, ceci est une trÚs mauvaise idée :

  • Duplication X fois de la logique (pour chaque type de client qui existe) Si le rĂšglement bancaire change ?

  • Il ne faut jamais faire confiance aux clients !

Logiquement, la plupart de la logique se trouvent plutĂŽt en interne, dans nos APIs :

C'est plus sĂ©curisĂ©, mais, on a potentiellement les mĂȘmes problĂšmes de maintenance. Il y a une duplication de la logique sur les diffĂ©rents APIs. Une solution potentielle est d'avoir un API monolithique, ou bien une librairie partagĂ©e entre toutes les implĂ©mentations.

Sinon, on peut héberger le logique business juste à cÎté des données :

Malgré l'API utilisé, la séquence nécessaire pour une opération reste inchangé. Et en plus, ni l'API ni le client voit les détails de l'opération.

Nous allons créer une procédure stockée qui agit sur plusieurs tables pour affecter un transfert d'argent. Voici le DDL pour les deux tables :

create table customer (
  customer_id int not null auto_increment,
  name varchar(256),
  surname varchar(256),
  balance decimal(10,2) not null default(0),
  primary key (customer_id)
);

create table journal (
  journal_id int not null auto_increment,
  customer_id int not null,
  amount decimal(10,2) not null default(0),
  date timestamp not null,

  primary key (journal_id),
  foreign key (customer_id) references customer(customer_id)
);

insert into customer (name, surname, balance) values ("Kevin", "Glass", 5000);
insert into customer (name, surname, balance) values ("Pierre", "Laurendeau", 7000);
insert into customer (name, surname, balance) values ("Florence", "Perrousset", 3500);

On peut rĂ©diger une fonction (en tant que stored procedure) qui peut ĂȘtre invoquĂ© Ă  tout moment, avec des paramĂštres, pour effectuer un transfert d'argent :

DELIMITER //
create procedure Transfer (amount decimal(10,2), from_id int, to_id int) modifies sql data
begin
  
  declare exit handler for sqlexception
    begin
      rollback;
      resignal;
    end;

  start transaction;

    select @soldeFrom:=balance from customer where customer_id=from_id for update;
    select @soldeTo:=balance from customer where customer_id=to_id for update;

    set @soldeTo:=@soldeTo + amount;
    set @soldeFrom:=@soldeFrom - amount;

    if @soldeFrom < 0 then
      signal sqlstate '45000' set message_text="Not enough available for transfer.";
    end if;

    update customer set balance = @soldeTo where customer_id=to_id;
    update customer set balance = @soldeFrom where customer_id=from_id;

    set @ts = CURRENT_TIMESTAMP();
    insert into journal (customer_id, amount, date) values (to_id, amount, @ts);
    insert into journal (customer_id, amount, date) values (from_id, -amount, @ts);


  commit;

end;
//
DELIMITER ;

Un délimiteur temporaire

On commence notre procédure stockée par :

DELIMITER //

En fait, ici, on change le symbole reconnu par l'interprĂšte SQL pour signaler la fin d'une commande. On le fait parce qu'on veut empĂȘcher l'interprĂšte d'interprĂ©ter les commandes qui se trouvent Ă  l'intĂ©rieur de notre procĂ©dure qu'on est en train de rĂ©diger !

Pour signaler la fin de déclaration de notre procédure, on évoque le délimiteur :

//

Une fois terminé, on rétablit le délimiteur :

DELIMITER ;

Commande pour la création d'une procédure

La procédure se crée avec une syntaxe précis :

create procedure NOM ( paramÚtres séparés par des virgules ) modifies sql data 
begin

   /** Les instructions de la procédure **/

end;

AprÚs la déclaration d'une procédure, on peut l'invoquer à tout moment avec :

call NOM(... paramĂštres ...);

Gestion d'erreurs

Il faut toujours penser aux erreurs qui peuvent arriver. Nous commençons donc par préciser que faire en cas d'erreur :

  declare exit handler for sqlexception
    begin
      rollback;
      resignal;
    end;

Dans cet exemple, on précise que dans le cas d'un sqlexception, on fait d'abord un rollback, puis on renvoie l'erreur plus haut dans le tas d'appels (avec resignal).

Comment signaler manuellement une erreur ? Avec la commande signal :

if @soldeFrom < 0 then
  signal sqlstate '45000' set message_text="Not enough available for transfer.";
end if;

Le state 45000 est réservé aux erreurs personnalisées.

Invoquer une procédure stockée

Nous commençons avec les données suivantes :

customer_id
name
surname
balance

1

Kevin

Glass

5000.00

2

Pierre

Laurendeau

7000.00

3

Florence

Perrousset

3500.00

On peut utiliser notre procĂ©dure afin d'effectuer un transfert de 2000 € de Kevin Ă  Pierre :

call Transfer(2000, 1, 2);
customer_id
name
surname
balance

1

Kevin

Glass

3000.00

2

Pierre

Laurendeau

9000.00

3

Florence

Perrousset

3500.00

Tout s'est bien passé !

Essayons avec une erreur :

call Transfer(5000, 1, 2);
ERROR 1644 (45000): Not enough available for transfer.

Lister, modifier une procédure

On peut récupérer la liste de procédures pour notre base de données (on filtre par base de données, car il y en a déjà d'autres qui existent dans le SGBDR) :

show procedure status where db = 'bank';

On peut récupérer le listing d'une procédure existante :

show create procedure Transfer;

On ne peut pas modifier directement les procédures. On est obligé de les supprimer et les remplacer avec une autre version.

drop procedure [nom];

Certains clients comme MySQL Workbench ont une interface qui facilite ces opérations.

Il est donc assez difficile à récupérer et modifier une procédure stockée. Pour cette raison, certains développeurs ne les aiment pas :

  • Pas trĂšs compatible avec la gestion de code moderne (ex. GIT)

  • Difficile Ă  distinguer les versions dĂ©ployĂ©es

Exercice

Vous ĂȘtes Ă©diteur d’une solution de publicitĂ© en ligne qui va rĂ©compenser l’internaute de l’attention passĂ©e sur les pubs (exemple, le navigateur Brave).

Au cƓur de votre solution est la logique business suivante :

  • Vous avez une base d’éditeurs partenaires (journaux, blogs, mĂ©dia, etc.). Chaque Ă©diteur classe leurs contenus par une ou plusieurs catĂ©gories : sport, actualitĂ©, divertissement, science, politique, enfant

  • Un annonceur de pub prĂ©paye un budget total pour une publicitĂ©. Pour chaque pub, l’annonceur prĂ©cise le prix-par-vu qu’il est prĂȘt Ă  payer par chaque catĂ©gorie cible. Plus le prix est Ă©levĂ©, plus la pub aura la chance d’apparaĂźtre dans un site Ă©diteur de ce genre.

  • Quand la pub apparaĂźt sur le site d’un Ă©diteur de contenu (journal, blog, etc.), le prix (correspondant Ă  la catĂ©gorie) est dĂ©duit du budget total de l’annonceur :

    • 75% est allouĂ© au compte de l’éditeur

    • 25% est allouĂ© au compte de l’internaute

CrĂ©ez le schĂ©ma pour votre solution, ainsi qu’une procĂ©dure SQL qui effectue le transfert de valeur au moment d’une vue sur une page web d’un Ă©diteur.

Votre procĂ©dure doit ĂȘtre tolĂ©rante aux fautes ainsi que des requĂȘtes concurrentes !

Attention : l’annonceur ne peut jamais avoir un solde nĂ©gatif.

Chaque partie prenante doit ĂȘtre capable de rĂ©cupĂ©rer son historique : ex. L’annonceur veut voir la rĂ©partition de dĂ©pense sur les Ă©diteurs, l’éditeur veut voir la rĂ©partition des sources de revenu, l’internaute veut voir l’historique de pubs dĂ©jĂ  vues.

Logique business : coté client
Logique business : coté API
Logique business : coté SGBDR