IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

PL/SQL Oracle Discussion :

Comment bloquer les Delete consécutives sur la même ligne?


Sujet :

PL/SQL Oracle

  1. #1
    Nouveau membre du Club
    Inscrit en
    Mai 2010
    Messages
    36
    Détails du profil
    Informations forums :
    Inscription : Mai 2010
    Messages : 36
    Points : 31
    Points
    31
    Par défaut Comment bloquer les Delete consécutives sur la même ligne?
    Bonjour,

    Dans une application métier développée par java/javascript, nous avons une fonctionnalité qui permet d'annuler un ticket déjà créé (un ticket ici représente un droit d'achat dont le montant est soustrait du solde client). Donc l'annulation d'un ticket, quand elle est acceptée, rétablit le solde client avec le montant du ticket qui a été déjà soustrait.
    Le problème c'est que pour des raisons de lenteur réseau, il se trouve que l'utilisateur clique sur le bouton annuler plusieurs fois et donc la requete d'annulation est envoyée plusieurs fois.
    Ces requêtes multiples d'annulation (processus), sur le même ticket, passent sans problème (selon comment c'est développé aujourd'hui) et font que le solde du client est rétabli plusieurs fois ce qui lui donne plus de ce qu'il doit. Exemple: si on annule un ticket de 100 euros alors qu'on arrive à cliquer 3 fois sur le bouton annuler (sachant que le bouton annuler doit disparaître de l'écran dès le premier clique) alors le solde du client est augmenté de 3 x 100 euros au lieu de 100 euros.

    Est ce que vous avez une idée de comment on peut verrouiller ce traitement de telle sorte qu'un seul process annulation puisse être exécuté?

    Voici en bas la procédure stockée Oracle appelée lors du clique sur le bouton annuler d'un ticket donné. Le p_num_bordereau est l'identifiant d'un ticket.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    Procedure annuler_ticket(p_num_bordereau IN VARCHAR2, NMES OUT NUMBER, LMES OUT VARCHAR2)
    Is
     
    V_ID_BORDEREAU NUMBER;
    v_montant Number;
    v_id_client number;
     
    Begin
     
    SELECT a.id_bordereau, montant, id_client
    INTO v_id_bordereau, v_montant, v_id_client
    FROM  chm.OPERATIONS a
    AND a.numero_bordereau = p_num_bordereau;
    --
    DELETE FROM chm.operations
    WHERE id_bordereau = v_id_bordereau;
    --
    Update chm.soldes set solde = solde + v_montant where id_client = v_id_client;
    --
    nmes := 0;
    lmes := 'OK';
    Exception
      When Others then
        nmes := 1;
        lmes := 'Ligne inexistante';
    End;

  2. #2
    McM
    McM est déconnecté
    Expert éminent

    Homme Profil pro
    Développeur Oracle
    Inscrit en
    Juillet 2003
    Messages
    4 580
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Oracle

    Informations forums :
    Inscription : Juillet 2003
    Messages : 4 580
    Points : 7 740
    Points
    7 740
    Billets dans le blog
    4
    Par défaut
    Bonjour,

    Normalement le second devrait sortir en erreur 1 : Ligne inexistante, sauf s'il s'agit de 2 sessions différentes et un commit lointain.


    Le mieux est de poser un verrou sur la ligne lors du SELECT, comme ça, tu es "sûr" que les autres sessions qui vont annuler le même p_num_bordereau vont attendre que le premier ait fini son commit


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    PROCEDURE annuler_ticket(p_num_bordereau IN VARCHAR2, NMES OUT NUMBER, LMES OUT VARCHAR2)
    IS
      v_id_bordereau number;
      v_montant number;
      v_id_client number;
    BEGIN
     
      SELECT a.id_bordereau, montant, id_client
      INTO v_id_bordereau, v_montant, v_id_client
      FROM chm.OPERATIONS a
      AND a.numero_bordereau = p_num_bordereau
      FOR UPDATE;
     
      DELETE chm.OPERATIONS
      WHERE id_bordereau = v_id_bordereau;
     
      UPDATE chm.soldes set solde = solde + v_montant
      WHERE id_client = v_id_client;
     
      nmes := 0;
      lmes := 'OK';
    EXCEPTION
    WHEN Others THEN
        nmes := 1; lmes := 'Ligne inexistante';
    END;
    Pourquoi le where du DELETE ne correspond pas au SELECT ? Tu DELETE plus de lignes ?
    Parce que du coup, le verrou sur 1 ligne et le DELETE sur plusieurs autres, tu peux avoir un interblocage, dans ce cas là, il faudrait poser un verrou NOWAIT sur toutes les lignes à DELETER
    Je ne met pas le code, mais je peux le fournir si tu en as besoin.
    More Code : More Bugs. Less Code : Less Bugs
    Mon Blog PL/Sql : Fichier Zip / Image BMP / Lire sqliteDB / QRCode et Images PNG ou BMP

  3. #3
    Expert éminent
    Avatar de pachot
    Homme Profil pro
    Developer Advocate YugabyteDB
    Inscrit en
    Novembre 2007
    Messages
    1 821
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Suisse

    Informations professionnelles :
    Activité : Developer Advocate YugabyteDB
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2007
    Messages : 1 821
    Points : 6 443
    Points
    6 443
    Billets dans le blog
    1
    Par défaut
    Bonjour,
    Le delete ne renvoit pas d'erreur lorsque la ligne n'est pas trouvée. Le nombre de lignes est dans SQL%ROWCOUNT
    Il faudrait gérer une transaction avec savepoint et rollback. Genre: savepoint; update ; delete ; rollback if rowcount=0;
    Franck Pachot - Developer Advocate Yugabyte 🚀 Base de Données distribuée, open source, compatible PostgreSQL
    🗣 twitter: @FranckPachot - 📝 blog: blog.pachot.net - 🎧 podcast en français : https://anchor.fm/franckpachot

  4. #4
    McM
    McM est déconnecté
    Expert éminent

    Homme Profil pro
    Développeur Oracle
    Inscrit en
    Juillet 2003
    Messages
    4 580
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur Oracle

    Informations forums :
    Inscription : Juillet 2003
    Messages : 4 580
    Points : 7 740
    Points
    7 740
    Billets dans le blog
    4
    Par défaut
    Pourquoi tu voudrais faire si compliqué avec des savepoint et un update avant le delete ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    DELETE ..
    IF sql%rowcount <> 0 THEN UPDATE END IF;
    Mais tout dépend si la condition where du SELECT est la même que le DELETE ou pas.
    More Code : More Bugs. Less Code : Less Bugs
    Mon Blog PL/Sql : Fichier Zip / Image BMP / Lire sqliteDB / QRCode et Images PNG ou BMP

  5. #5
    Rédacteur

    Avatar de SQLpro
    Homme Profil pro
    Expert bases de données / SQL / MS SQL Server / Postgresql
    Inscrit en
    Mai 2002
    Messages
    21 772
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Expert bases de données / SQL / MS SQL Server / Postgresql
    Secteur : Conseil

    Informations forums :
    Inscription : Mai 2002
    Messages : 21 772
    Points : 52 732
    Points
    52 732
    Billets dans le blog
    5
    Par défaut
    Il suffit d'annuler la transaction si le nombre de ligne mis à jour dans la première requête est de zéro.

    C'est le B A BA de la gestion des transactions !

    1) pas de ligne mise à jour … que faire ?
    2) erreur lors de la mise à jour … que faire ?

    A +
    Frédéric Brouard - SQLpro - ARCHITECTE DE DONNÉES - expert SGBDR et langage SQL
    Le site sur les SGBD relationnels et le langage SQL: http://sqlpro.developpez.com/
    Blog SQL, SQL Server, SGBDR : http://blog.developpez.com/sqlpro
    Expert Microsoft SQL Server - M.V.P. (Most valuable Professional) MS Corp.
    Entreprise SQL SPOT : modélisation, conseils, audit, optimisation, formation...
    * * * * * Expertise SQL Server : http://mssqlserver.fr/ * * * * *

  6. #6
    Membre chevronné
    Homme Profil pro
    Chef de projet MOA
    Inscrit en
    Février 2012
    Messages
    652
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Morbihan (Bretagne)

    Informations professionnelles :
    Activité : Chef de projet MOA
    Secteur : Distribution

    Informations forums :
    Inscription : Février 2012
    Messages : 652
    Points : 1 878
    Points
    1 878
    Par défaut
    N'y a t-i pas un soucis dans la requête SELECT aussi ?


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    SELECT a.id_bordereau, montant, id_client
      INTO v_id_bordereau, v_montant, v_id_client
      FROM chm.OPERATIONS a
      AND a.numero_bordereau = p_num_bordereau
    Sinon je rejoins les solutions à base de contrôle sur le SQL%ROWCOUNT qui doit aiguiller sur l'UPDATE ou non

  7. #7
    Expert confirmé
    Profil pro
    Inscrit en
    Août 2008
    Messages
    2 947
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2008
    Messages : 2 947
    Points : 5 846
    Points
    5 846
    Par défaut
    C'est pas le DELETE qui va planter, c'est le SELECT INTO.

    Comme le SELECT est nécessaire pour l'UPDATE, faites comme vous a proposé MCM :
    Rajouter un FOR UPDATE au SELECT INTO

    Ensuite, gérer l'exception NO_DATA_FOUND => ne rien faire ou logguer ou envoie d'une erreur personnalisée

    Et rajouter un RAISE à votre WHEN OTHERS (ou supprimez le), il faut faire remonter l'exception au programme appelant.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    PROCEDURE annuler_ticket(p_num_bordereau IN VARCHAR2, NMES OUT NUMBER, LMES OUT VARCHAR2)
    IS
      v_id_bordereau number;
      v_montant number;
      v_id_client number;
    BEGIN
     
      SELECT a.id_bordereau, montant, id_client
      INTO v_id_bordereau, v_montant, v_id_client
      FROM chm.OPERATIONS a
      where a.numero_bordereau = p_num_bordereau
      FOR UPDATE;
     
      DELETE chm.OPERATIONS
      WHERE id_bordereau = v_id_bordereau;
     
      UPDATE chm.soldes set solde = solde + v_montant
      WHERE id_client = v_id_client;
     
      nmes := 0;
      lmes := 'OK';
    EXCEPTION
    when no_data_found then
    	NULL;
    /*
    WHEN Others THEN
        nmes := 1; lmes := 'Ligne inexistante';
    */
    END;

  8. #8
    Expert éminent
    Avatar de pachot
    Homme Profil pro
    Developer Advocate YugabyteDB
    Inscrit en
    Novembre 2007
    Messages
    1 821
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Suisse

    Informations professionnelles :
    Activité : Developer Advocate YugabyteDB
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2007
    Messages : 1 821
    Points : 6 443
    Points
    6 443
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par McM Voir le message
    Pourquoi tu voudrais faire si compliqué avec des savepoint et un update avant le delete ?
    Parce que je vois un affreux 'when others' et j'ai peur que des modifications faites à moitié soient ensuite commit-ées.
    Et que je trouve ça moins compliqué, de bien indiquer le début et la fin de ce qui est atomique. Et aussi de toucher au solde en premier.
    Franck Pachot - Developer Advocate Yugabyte 🚀 Base de Données distribuée, open source, compatible PostgreSQL
    🗣 twitter: @FranckPachot - 📝 blog: blog.pachot.net - 🎧 podcast en français : https://anchor.fm/franckpachot

Discussions similaires

  1. Réponses: 0
    Dernier message: 31/07/2014, 12h26
  2. Comment afficher une liste sur la même ligne avec tous les navigateurs
    Par Alexandrebox dans le forum Mise en page CSS
    Réponses: 1
    Dernier message: 25/07/2010, 03h05
  3. Réponses: 6
    Dernier message: 29/05/2008, 12h06
  4. Comment bloquer les photos sur mon site
    Par footeuse13 dans le forum Sécurité
    Réponses: 5
    Dernier message: 08/08/2007, 16h54
  5. Réponses: 3
    Dernier message: 07/02/2007, 17h39

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo