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

MS SQL Server Discussion :

Efficacite de CASE dans un UPDATE ?


Sujet :

MS SQL Server

  1. #1
    Membre à l'essai
    Inscrit en
    Janvier 2005
    Messages
    61
    Détails du profil
    Informations forums :
    Inscription : Janvier 2005
    Messages : 61
    Points : 24
    Points
    24
    Par défaut Efficacite de CASE dans un UPDATE ?
    Bonjour a tous,

    en fouillant rapidement les possibilites de transact-sql (il y en a quand meme pas mal et on en oublie souvent), je suis tombe sur CASE que je n'avais jusqu'a present jamais utilise.

    Or nous avons dans un jeu, une table definissant la liste des "items" possedes par les personnages, avec pour chaque item une ligne de la forme :
    - le nom du personnage qui possede
    - l'index de l'objet dans l'inventaire du personnage
    - le nombre d'utilisations restantes
    - le Status de l'objet (0 indisponible, 1 dans l'inventaire...)

    On utilise beaucoup ce Status, et donc lorsqu'un joueur utilise un Item, on commence par reduire le nombre d'utilisations restantes par un update, puis si ce nombre devient nul, on fait un nouvel update pour rendre l'objet indisponible (requete de la forme suivante en gros) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    UPDATE tblObjets SET Restant = Restant - 1 WHERE Perso = @lePerso AND Index=@indexObjet AND Status <> 0
    UPDATE tblObjets SET Status = 0 WHERE Restant = 0 Perso = @lePerso AND Index=@indexObjet AND Status <> 0
    Mon idee serait donc de reunir tout ca sous une seule requete, qui update les 2 nombres a la fois (si le nombre restant est 1, on sait qu'il finira a 0 apres l'update et donc, on place le status a 0 aussi car l'objet devient inutilisable)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    UPDATE tblObjets SET Status = CASE WHEN Restant = 1 THEN 0 ELSE 1 END, Restant = Restant - 1 WHERE Perso = @lePerso AND Index=@indexObjet AND Status <> 0
    (J'espere que les exemples de code sont clairs)

    Voila donc le concept. Mais mon collegue pense que l'utilisation de CASE ralentit trop le process et serait en realite plus lent que faire les 2 update a la suite, ce dont je ne suis pas du tout certain. Du coup, j'aurais aime savoir ce qui selon vous (par experience si possible ^^) est le plus rapide et efficace : avec ou sans CASE ?

    Merci d'avance.

    ps: oui je sais, on pourrait supprimer Status et juste utiliser "Restant" pour tester la dispo ou pas de l'item (si different de 0, hop l'item est dispo nickel), mais il se trouve que ce n'est pas possible dans la majorite des cas car status a plus de 2 valeurs dans le cas reel, donc une reelle signification plus que binaire ^_^

  2. #2
    Membre expérimenté

    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    1 298
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 1 298
    Points : 1 578
    Points
    1 578
    Par défaut Juste un raisonnement
    Pour ce que ça vaut, car je ne connais pas le SGBD.

    Faire 2 update implique de lire 2 fois la même table.

    AMHA, avec le CASE, on doit ralentir légèrement le traitement car le moteur SQL doit effectuer le test, mais en revanche on ne lit qu'une seule fois la table, donc on doit en final gagner du temps.


  3. #3
    Membre à l'essai
    Inscrit en
    Janvier 2005
    Messages
    61
    Détails du profil
    Informations forums :
    Inscription : Janvier 2005
    Messages : 61
    Points : 24
    Points
    24
    Par défaut
    C'est aussi ce que je pense. Et c'est un peu ce que la simple logique aurait tendance a dicter, car une fois la ligne lue, le traitement pour le Case ne semble pas tres lourd.

    Toutefois mon collegue me soutient que le CASE est relativement lourd a traiter pour le serveur et qu'il vaut mieux l'eviter. C'est aussi possible si ca sort des attributions classiques des fonctions de base internes au serveur, mais pour ca je n'ai pas vraiment de moyen de verifier a part chercher l'experience la ou elle se trouve... ici (enfin il me semble ^_^).

    Du coup si quelqu'un a une idee sur cet epineux probleme d'optimisation, je suis preneur.

    ps: merci Mercure pour la reponse, je commencais a m'inquieter avec 2 sujets basiques et clairs sans aucune reponse ..

  4. #4
    Membre expérimenté

    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    1 298
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 1 298
    Points : 1 578
    Points
    1 578
    Par défaut Test de 2 requêtes
    Je ferais alors un test de perf. sur chaque requête, de nuit pour ne pas perturber les traitements de journée.

    Sur un échantillon bien représentatif des données, voire sur l'ensemble de la table, je mesurerais programmatiquement le temps que SQL passe (durée et CPU) à faire les 2 updates puis le temps qu'il passe à faire la requête avec CASE.
    Je comparerais ensuite les résultats et je serais fixé.

    Ces tests me permettraient ainsi de confirmer l'hypothèse probable du gain de temps auprès de mon collègue ou au contraire de l'infirmer.

  5. #5
    Membre actif Avatar de neuropathie
    Inscrit en
    Janvier 2005
    Messages
    223
    Détails du profil
    Informations forums :
    Inscription : Janvier 2005
    Messages : 223
    Points : 201
    Points
    201
    Par défaut
    Selon les best practices et les retours d'expériences que j'ai pu consulter, l'instruction UPDATE est lourde et est à éviter pour les applis web, par contre pour les clients riches elle est utile. donc un UPDATE avec le CASE, je crois que c'est déconseillé

  6. #6
    Membre averti Avatar de Rei Angelus
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2006
    Messages
    292
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mars 2006
    Messages : 292
    Points : 356
    Points
    356
    Par défaut
    Je travaille sur SYBASE et j'utilise régulièrement le 'CASE'. Cependant, cela me pose régulièrement des problèmes de temps de traitement qui m'obligent à revenir à des requêtes simples (si possible).

    A mon avis les 2 UPDATE seront beaucoup plus rapides que le UPDATE CASE.

    Après effectivement, il est judicieux de faire des tests avec ton SGBD et ta base pour en avoir confirmation.

  7. #7
    Membre à l'essai
    Inscrit en
    Janvier 2005
    Messages
    61
    Détails du profil
    Informations forums :
    Inscription : Janvier 2005
    Messages : 61
    Points : 24
    Points
    24
    Par défaut
    Rei : merci, c'est a peu pres les propos que tient mon collegue a propos de MS SQL server

    neuropathie : pourquoi cette difference entre appli lourdes et client leger (web) ? Les instructions SQL, de tte facon, sont executees par le serveur (dans une architecture 3 tiers tout du moins - peut etre parles tu de clients qui impacteraient directement la base, si ca existe ? -)... je n'ai pas tres bien compris ce que tu voulais me dire, desole ^_^;;

    Mercure : ok merci, j'attends tes resultats avec impatience . Il faudrait que je trouve le temps pour faire de meme, mais ca risque d'attendre un peu...

    Le probleme n'a pas l'air si trivial finalement, je suis bien content d'etre venu poser la question...

  8. #8
    Rédacteur
    Avatar de pcaboche
    Homme Profil pro
    Inscrit en
    Octobre 2005
    Messages
    2 785
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Singapour

    Informations forums :
    Inscription : Octobre 2005
    Messages : 2 785
    Points : 9 716
    Points
    9 716
    Par défaut
    Citation Envoyé par k'n1
    Mais mon collegue pense que l'utilisation de CASE ralentit trop le process
    Dans la plupart des SGBD, l'utilisation d'un CASE dans une jointure ralentit énormément le temps d'exécution de la requête puisque le SGBD ne peut plus alors se baser sur les index des tables.

    Hors d'une jointure, l'exécution est ralentie, mais dans une moindre mesure (2 UPDATE risquent tout de même d'être plus rapides).

    Il y a sûrement quelque chose à améliorer dans le modèle pour garantir qu'à tout moment, on ait bien Restant = 0 -> Statut = 0 sans devoir le spécifier pour chaque fois (par une vue, par exemple).
    "On en a vu poser les armes avant de se tirer une balle dans le pied..."
    -- pydévelop

    Derniers articles:

    (SQL Server) Introduction à la gestion des droits
    (UML) Souplesse et modularité grâce aux Design Patterns
    (UML) Le Pattern Etat
    Autres articles...

  9. #9
    Membre du Club
    Profil pro
    Inscrit en
    Décembre 2002
    Messages
    120
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2002
    Messages : 120
    Points : 69
    Points
    69
    Par défaut
    A titre personnel je proscrirais le Case dans un update au même titre qu'un OR dans les trois quart des ordres SQL que je remplace fort volontier par un Union dès que cela est possible ...

    D'un point de vue physique le case va empecher d'utiliser les index (sauf si tu fais un index fonction ... auquel cas il faudrait rechercher un peu plus loin) et donc faire un fetch complet de ta table pour faire les update.

    Deux updates bien placés sur des clés seront donc beaucoup plus efficaces à mon avis !

  10. #10
    Membre à l'essai
    Inscrit en
    Janvier 2005
    Messages
    61
    Détails du profil
    Informations forums :
    Inscription : Janvier 2005
    Messages : 61
    Points : 24
    Points
    24
    Par défaut
    pcaboche, scornille > merci pour vos reponses qui se rejoignent.

    Toutefois, je ne comprend pas pourquoi l'utilisation des index est impossible lors de l'utilisation du CASE, tout du moins dans le cas present, ni pourquoi il faudrait faire 2 fetch de la table ?

    dans l'exemple cite, la table a un index sur 'Perso' et 'Index' il me semble. On voit que ces 2 elements se trouvent bien dans la clause Where donc pas de soucis. Par contre, on n'utilise pas de case du le 'Status' car il a tendance a pas mal changer, ce qui ralentirait pas mal l'utilisation de la table.

    De plus, l'element sur lequel porte le CASE est un element du tuple sur lequel on travail (en l'occurence, toujours unique). A premiere vue, l'operation me parait extremement legere (le tuple a ete lu dans son ensemble (n'est-ce pas ?), vu qu'il a ete trouve par la clause Where, et il a ete trouve rapidement grace aux index).

    Je pense qu'il doit me manquer un element de raisonnement, mais je ne vois pas lequel : pour moi il s'agit de l'utilisation classique d'un tuple d'une table (avec ses index) ou l'on verifie simplement un des elements ?

    merci d'avance pour vos eclaircissement a cet esprit embrume par le metro matinal... ^_^;

    ps: je ne sais pas grand chose sur les vues (on les utilise tres peu ici) ... mais ca m'interesse

  11. #11
    Rédacteur
    Avatar de WOLO Laurent
    Homme Profil pro
    Architecte de base de données
    Inscrit en
    Mars 2003
    Messages
    2 741
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : Congo-Brazzaville

    Informations professionnelles :
    Activité : Architecte de base de données
    Secteur : Finance

    Informations forums :
    Inscription : Mars 2003
    Messages : 2 741
    Points : 4 414
    Points
    4 414
    Par défaut
    C'est bien connu dans les règles d'écriture des ordres SQL optimisés :
    Il vaut mieux privilégier la logique ensembliste par rapport à la logique procédurale. Plus simplement, Le case serait plus lent que son équivalent sans update.
    Vous voulez maintenant vous rassurez : Lancer le plan d'execution de ces deux requêtes et nous allons vous aidez dans l'analyse de ceux-ci.

    Découvrez la FAQ de MS SQL Server.
    La chance accorde ses faveurs aux esprits avertis !

  12. #12
    Membre confirmé
    Avatar de Mindiell
    Profil pro
    Inscrit en
    Juin 2006
    Messages
    735
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2006
    Messages : 735
    Points : 546
    Points
    546
    Par défaut
    Moi je dirais qu'il vaut mieux approfondir ses connaissances de T-SQL

    Voilà une solution avec un seul update et sans case :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    UPDATE tblObjets 
    SET Status = SIGN ( Restant - 1 )
    , Restant = Restant - 1 
    WHERE Perso = @lePerso 
    AND Index = @indexObjet 
    AND Status <> 0
    Et voilà ! La fonction SIGN te retourne 1 en cas de valeur positive et 0 en cas de 0. A priori, le -1 retourné en cas de valeur négative ne sera jamais retourné puisque le Restant minimum est de 0, non ?

    Et hop !
    Mindiell
    "Souvent, femme barrit" - Elephant man

  13. #13
    Membre à l'essai
    Inscrit en
    Janvier 2005
    Messages
    61
    Détails du profil
    Informations forums :
    Inscription : Janvier 2005
    Messages : 61
    Points : 24
    Points
    24
    Par défaut
    Merci pour les reponses.

    Mindiell: tu as surement raison, sauf que :

    - j'ai dit un peu plus haut que 'STATUS' pouvait valoir d'autre valeurs (principalement 1 et 2). Ca ne fonctionne donc pas dans ce cas la (ok, ca marche surement avec un 'Status = Status * SIGN ( Restant - 1 )' ...)

    - mais SIGN pour remplacer CASE, ca me parait pas moins procedural, si ?

    WOLO Laurent : quand je lance le plan d'execution, il me dit que chaque update prend autant de temps que mon update avec le case (j'aurais du mal a donner plus de precision, mon Entreprise Manager est en coreen :-/ et je ne connais pas le vocabulaire de EM en francais ^_^)

  14. #14
    Membre confirmé
    Avatar de Mindiell
    Profil pro
    Inscrit en
    Juin 2006
    Messages
    735
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2006
    Messages : 735
    Points : 546
    Points
    546
    Par défaut
    Ton code sans update fais ca :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    UPDATE tblObjets 
    SET STATUS = CASE 
        WHEN Restant = 1 
        THEN 0 
        ELSE 1 
    END
    , Restant = Restant - 1 
    WHERE Perso = @lePerso 
    AND INDEX=@indexObjet 
    AND STATUS <> 0
    Tu forces donc Status à 0 ou à 1. Je fais la même chose, non ? Maintenant, si ce que tu veux faire est différent, montre nous une requête différente

    Pour le reste, SIGN est une fonction, pas du SQL. Sinon, tu peux aussi faire un trigger à chaque update qui vérifie le nouveau résultat de "Restant". Ainsi tu fais juste ton update, et tu continues ton programme. C'est la base derrière qui fait du boulot automatiquement...
    Mindiell
    "Souvent, femme barrit" - Elephant man

  15. #15
    Rédacteur
    Avatar de pcaboche
    Homme Profil pro
    Inscrit en
    Octobre 2005
    Messages
    2 785
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Singapour

    Informations forums :
    Inscription : Octobre 2005
    Messages : 2 785
    Points : 9 716
    Points
    9 716
    Par défaut
    Citation Envoyé par Mindiell
    Sinon, tu peux aussi faire un trigger à chaque update qui vérifie le nouveau résultat de "Restant". Ainsi tu fais juste ton update, et tu continues ton programme. C'est la base derrière qui fait du boulot automatiquement...
    Cela rejoint cette idée:
    Citation Envoyé par pcaboche
    Il y a sûrement quelque chose à améliorer dans le modèle pour garantir qu'à tout moment, on ait bien Restant = 0 -> Statut = 0 sans devoir le spécifier pour chaque fois (par une vue, par exemple).
    La différence entre les 2 solutions se trouve dans la réponse à cette question: "doit-on stocker les données calculées ?".

    Dans le cas où les données calculées dépendent de nombreux paramètres, il vaut mieux stocker le résultat (d'où utilisation d'un trigger). Dans le cas où les données calculées sont simples, ça dépend de ce qu'on en fait (si on utilise cette donnée dans d'autres calculs ou non). L'important, c'est de garantir l'intégrité des données.
    "On en a vu poser les armes avant de se tirer une balle dans le pied..."
    -- pydévelop

    Derniers articles:

    (SQL Server) Introduction à la gestion des droits
    (UML) Souplesse et modularité grâce aux Design Patterns
    (UML) Le Pattern Etat
    Autres articles...

  16. #16
    Membre à l'essai
    Inscrit en
    Janvier 2005
    Messages
    61
    Détails du profil
    Informations forums :
    Inscription : Janvier 2005
    Messages : 61
    Points : 24
    Points
    24
    Par défaut
    En effet, mon clavier a fourche ^_^ ...

    Est-ce qu'un trigger, dans ce genre de cas, est plus efficace et plus rapide que l'utilisation de SIGN ?

  17. #17
    Membre à l'essai
    Inscrit en
    Janvier 2005
    Messages
    61
    Détails du profil
    Informations forums :
    Inscription : Janvier 2005
    Messages : 61
    Points : 24
    Points
    24
    Par défaut
    Citation Envoyé par pcaboche
    La différence entre les 2 solutions se trouve dans la réponse à cette question: "doit-on stocker les données calculées ?".

    Dans le cas où les données calculées dépendent de nombreux paramètres, il vaut mieux stocker le résultat (d'où utilisation d'un trigger). Dans le cas où les données calculées sont simples, ça dépend de ce qu'on en fait (si on utilise cette donnée dans d'autres calculs ou non). L'important, c'est de garantir l'intégrité des données.
    ok. Toutefois, je dois me meprendre sur le terme "donnees calculees". En effet, de ce que j'en vois il s'agit d'un update, donc ces donnees sont forcement stockees (dans la table et la/les ligne sur laquelle on fait l'update)... peut-etre parles-tu de l'utilisation de "Status" dans la suite de la procedure stockee ou se trouverait l'update ?

  18. #18
    Membre à l'essai
    Inscrit en
    Janvier 2005
    Messages
    61
    Détails du profil
    Informations forums :
    Inscription : Janvier 2005
    Messages : 61
    Points : 24
    Points
    24
    Par défaut
    pcaboche ?

    ps: decidement il est bien difficile de se faire entendre quand il y a trop de monde actif sur un forum...

  19. #19
    Rédacteur
    Avatar de pcaboche
    Homme Profil pro
    Inscrit en
    Octobre 2005
    Messages
    2 785
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Singapour

    Informations forums :
    Inscription : Octobre 2005
    Messages : 2 785
    Points : 9 716
    Points
    9 716
    Par défaut
    Citation Envoyé par k'n1
    pcaboche ?

    ps: decidement il est bien difficile de se faire entendre quand il y a trop de monde actif sur un forum...
    Oui, désolé je t'ai un peu oublié.

    Ecoute, fait ce qui te semble le plus simple, mais personnellement ça m'embêterait de devoir mettre
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    STATUS = CASE 
        WHEN Restant = 1 
        THEN 0 
        ELSE 1 
    END
    dans chacune de mes requêtes UPDATE/INSERT.

    D'une façon ou d'une autre (trigger, procédure stockée, vue...), je m'arrangerais pour que cette condition soit vérifiée à tout moment, sans effort de programmation supplémentaire.
    "On en a vu poser les armes avant de se tirer une balle dans le pied..."
    -- pydévelop

    Derniers articles:

    (SQL Server) Introduction à la gestion des droits
    (UML) Souplesse et modularité grâce aux Design Patterns
    (UML) Le Pattern Etat
    Autres articles...

  20. #20
    Membre à l'essai
    Inscrit en
    Janvier 2005
    Messages
    61
    Détails du profil
    Informations forums :
    Inscription : Janvier 2005
    Messages : 61
    Points : 24
    Points
    24
    Par défaut
    ok, merci pour l'info ^_^

    Resultat (au cas ou ca interesserait encore quelqu'un ou pour notre ami gougueul ^_^) :
    - j'ai utilise le "Status = Status * SIGN (Restant)"

    A explorer :
    - utilisation de trigger
    - utilisation de vue


    ndnmdc: une petite question bonus qui me titille depuis un moment mais dont je n'ai pas encore trouve la reponse, relativement simple...

    je sais que l'utilisation d'une fonction (comprenant une requete) a l'interieur d'une SP est plus lent en general qu'une simple SP, car la fonction n'entre pas dans le plan d'execution (et donc dans le cache). Mais qu'en est-il d'une sous-procedure (ie une SP dans une autre SP) ? Est-ce moins efficace qu'une SP d'un bloc ?

    merci pour vos reponse et d'avance pour cette derniere question.

    edit: pfiu que veux tu c'est l'emotion ...

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. Mette a jour 2 colonnes dans un UPDATE??
    Par Devil666 dans le forum Langage SQL
    Réponses: 3
    Dernier message: 19/07/2005, 15h02
  2. REPLACE dans un UPDATE
    Par outdial dans le forum Requêtes
    Réponses: 3
    Dernier message: 20/09/2004, 11h00
  3. Une ch'tite bizarrerie dans les Updates de SQL Server
    Par Wakko2k dans le forum MS SQL Server
    Réponses: 12
    Dernier message: 08/04/2004, 14h14
  4. GROUP BY et HAVING dans un UPDATE
    Par MashiMaro dans le forum Langage SQL
    Réponses: 3
    Dernier message: 26/08/2003, 08h03
  5. Réponses: 6
    Dernier message: 26/01/2003, 13h45

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