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

SQL Procédural MySQL Discussion :

Swap entre deux colonnes pour 2 enregistrements


Sujet :

SQL Procédural MySQL

  1. #1
    Rédacteur
    Avatar de Erakis
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Octobre 2003
    Messages
    523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Canada

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2003
    Messages : 523
    Points : 233
    Points
    233
    Par défaut Swap entre deux colonnes pour 2 enregistrements
    Bonjour,

    Voici un apperçu de ma table :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    ID    User     Order
    ------------------------
    1        1          3
    2        4          4
    3        3          1
    4        2          2
    Dans une page web, j'ai une liste d'items ayant chacun deux petites images permettant de les ré-ordonner. Autrement dit un UP/DOWN. Si l'utilisateur clique sur UP pour un item, je dois décrémenter son Order de -1 et incrémenter l'item au-dessus de lui de +1. Comme indice j'ai le ID de l'item à incrémenter. Similaire pour le DOWN mais en inversé bien sur.

    Alors ma question c'est : Est-ce que c'est possible de faire ce traitement en UNE seule requête (sans stored proc).

    Merci.

  2. #2
    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 Re: Swap entre deux colonnes pour 2 enregistrements
    Oui, c'est possible.

    Pour le UP:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    UPDATE table
    SET Order=IF(Order=$num, Order-1, Order+1)
    WHERE Order=$num OR Order=$num-1

    Pour le DOWN:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    UPDATE table
    SET Order=IF(Order=$num, Order+1, Order-1)
    WHERE Order=$num OR Order=$num+1

    On en déduit une formule générale, avec un $delta tel que:
    - pour UP: $delta=1
    - pour DOWN: $delta=-1 :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    UPDATE table
    SET Order=IF(Order=$num, Order-$delta, Order+$delta)
    WHERE Order=$num OR Order=$num-$delta
    ... comme ça, ça évite d'écrire deux fois la même requête (on change juste le $delta).

  3. #3
    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
    On peut aussi faire une formule encore plus générale, permettant d'échanger 2 éléments de n'importe quel rang:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    UPDATE table
    SET Order=IF(Order=$rang1, $rang2, $rang1)
    WHERE Order=$rang1 OR Order=$rang2

  4. #4
    Rédacteur
    Avatar de Erakis
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Octobre 2003
    Messages
    523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Canada

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2003
    Messages : 523
    Points : 233
    Points
    233
    Par défaut
    Bonjour,

    Wow ! Exactement ce que je tentais de faire, j'étais pas loin côté code SQL mais cela ne fonctionnait pas. Maintenant en plus de me fournir une solution tu m'offres même des extras ! pcaboche t'es trop fort. Un gros merci, j'avais besion d'une solution pour hier. Merci !

    PS : Il me semble que tu es très actif sur ce forum, du moins depuis que je pose des questions tu y répond très souvent... Tu devrais peut-être postuler pour être MODÉRATEUR non ?

  5. #5
    Rédacteur
    Avatar de Erakis
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Octobre 2003
    Messages
    523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Canada

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2003
    Messages : 523
    Points : 233
    Points
    233
    Par défaut
    Bonjour,

    Je viens d'essayer le tout et je me suis rendu compte que cette requête n'est pas exactement ce que j'ai besion.

    Prenons le cas où un utilisateur ouvre la page web avec la liste des items et qu'en même temps un autre utilisateur y accède aussi. Le premier décide d'aller chercher un verre d'eau et l'autre déplace les items à plusieurs reprises. Lorsque le premier utilisateur reviendra il aura sur sa page l'ancienne ordre des items car la page n'aura pas été rafraîchie (postback). Donc s'il désire effectuer un UP sur un item, j'ai son ordre actuel mais ce n'est plus la bonne car l'autre utilisateur l'a déplacé. Toutefois la seul chose qui restera INCHANGER sera le ID des items.

    Alors côté code (c#) j'aimerais avoir une fonction du genre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    ChangerOrdre( ItemID, Direction )
    {
      ExecuterRequete( "UPDATE...;" )
    }
    Maintenant si le premier utilisateur demande un déplacement de l'item ayant le ID 4 vers le haut je ferai :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    ChangerOrdre( 4, Haut )
    De cette manière, peut-importe où l'item se trouve, l'ordre de l'item #4 sera échanger avec l'ordre de l'item juste au dessus de lui, donc l'ordre de l'item #4 moins 1.

    Dernière chose, l'ordre des items doit être entre 1 et n. Donc ne doit en aucun cas être inférieure à zéro. Et l'ordre doit être linéaire, donc 1,2,3,4,5 et non 1,2,3,5,6

    Merci à tous.

  6. #6
    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
    J'avais pensé à certains de ces problèmes, mais après-coup seulement. Après, j'ai eu un problème avec ma connection Internet et donc je n'ai pas pu accéder au site de la journée.

    Ce que tu décris complique de beaucoup le problème, mais je pense avoir trouvé une solution pour le UP, tiens-toi bien:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    UPDATE matable A
     
    INNER JOIN matable B
    ON (B.Order=A.Order OR B.Order = A.Order-1)
     
    LEFT OUTER JOIN matable C
    ON (ABS(B.Order-C.Order)=1 AND (C.Order=A.Order OR C.Order=A.Order-1))
     
    SET B.Order=C.Order
     
    WHERE A.Id=$id
    AND C.Order IS NOT NULL
    Je l'ai testée sous MySQL 4.1 et ça a l'air de marcher.

    Le principe:
    - On part de la table A pour récupérer l'ordre de l'enregistrement d'Id $id
    - La table B ressort cet enregistrement ainsi que l'enregistrement juste au-dessus (s'il existe)
    - La table C permet de "croiser" les enregistrements (s'ils existent)
    - On teste si on a bien 2 enregistrements avec: "WHERE C.id IS NOT NULL"
    - Si on a bien 2 enregitrements, on affecte la valeur de C à B. Si on n'a pas 2 enregistrements, aucune ligne n'est affectée.


    Citation Envoyé par Erakis
    PS : Il me semble que tu es très actif sur ce forum, du moins depuis que je pose des questions tu y répond très souvent... Tu devrais peut-être postuler pour être MODÉRATEUR non ?
    Tu es la deuxième personne à me suggérer l'idée, faudra que j'y pense (auquel cas, il faudra m'éclairer sur les droits et devoirs liés à cette fonction).

    Cela ne fait pas longtemps que je suis sur le forum, mais j'y passe beaucoup de temps. En fait, je suis au chômage et j'essaye de trouver du boulot, mais c'est pas facile (surtout que je suis jeune diplômé, soi-disant "sans expérience" !). Alors plutôt que de tourner en rond entre 4 murs et de déprimer, je viens sur le forum et j'essaye d'aider les autres. Au moins, j'ai moins l'impression de servir à quelque chose (et ça me donne une bonne idée de mes compétences réelles)

    Voilà, tu sais tout maintenant !

  7. #7
    Rédacteur
    Avatar de Erakis
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Octobre 2003
    Messages
    523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Canada

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2003
    Messages : 523
    Points : 233
    Points
    233
    Par défaut
    J'ai du retirer l'index UNIQUE sur mon champ ORDER et tout fonctionne à merveille Un gros gros merci !

    J'ai tenter créer la requête pour le DOWN mais ça fonctionne pas vraiment.

    Par contre, je dois m'assurer que ces l'ordres seront toujours LINÉAIRE.
    Donc 1,2,3,4,5 et non 1,2,3,5,6.
    Reprenons les deux utilisateurs précédant, un suprime l'item 4 et l'autre décide faire un UP sur l'item 5 par la suite. La clause -1 ne sera pas valide. Mais je crois que je vais m'organiser de sorte que lorsqu'il y aura un AJOUT ou une SUPPRESSION, je les RÉ-ordonnerai tous correctement.

  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 Erakis
    je vais m'organiser de sorte que lorsqu'il y aura un AJOUT ou une SUPPRESSION, je les RÉ-ordonnerai tous correctement.
    On a eu une question équivalente il y a un petit moment. En cas de suppression:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    UPDATE matable
    SET Order=Order-1
    WHERE Order>$orderSupprime
    Ca doit être possible de faire une jointure pour tenir compte de l'Id.

  9. #9
    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
    Voilà la requête:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    UPDATE 
    matable A
    INNER JOIN matable B ON (A.Order<B.Order)
     
    SET B.Order=B.Order-1
     
    WHERE A.Id=$idSupprime

  10. #10
    Rédacteur
    Avatar de Erakis
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Octobre 2003
    Messages
    523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Canada

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2003
    Messages : 523
    Points : 233
    Points
    233
    Par défaut
    J'ai une dernière question pour toi pcaboche.
    J'aurais peut-être du partir un nouveau post mais je te la pose quand même. Soyons la procédure stockée suivante : "spAjouterIntervenant"

    Si deux utilisateurs exécutent cette procédure (via le site web) en même temps, comment MySQL réagira ?

    - Les exécutera-t-il une après l'autre ?
    - Est-ce que les tables impliqués seront lockés ?

    En plus claire :
    Je désire ajouter un intervenant dans une table. Pour débuter je vérifie si l'utilisateur conçerné existe dans la table des utilisateurs, si non alors on arrête tout et je retourne un code d'erreur. S'il existe alors on vérifie aussi s'il existe déjà dans la liste des intervenants. Si oui alors je retourne un code d'erreur, si non alors on l'ajoute et c'est un succès.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
      IF EXISTS( SELECT UserID FROM Users.... ) THEN
         IF NOT EXISTS( SELECT IntervenantID FROM Intervenants.... ) THEN
             # On ajoute l'intervenant
         ELSE
             SET ERROR_CODE = 2;
         END IF;        
      ELSE   
         SET ERROR_CODE = 1;
      END IF;
    Durant l'exécution de TOUT ce code, est-ce qu'un intervenant peut-être supprimé par un autre traitement (un autre utilisateur sur le web ?)

    Merci

  11. #11
    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 Erakis
    J'ai une dernière question pour toi pcaboche.
    J'aurais peut-être du partir un nouveau post mais je te la pose quand même.
    En effet, en général question différente=fil différent. C'est plus pratique pour les recherches et je ne suis pas un spécialiste des procédures stockées sous MySQL.

    Citation Envoyé par Erakis
    Durant l'exécution de TOUT ce code, est-ce qu'un intervenant peut-être supprimé par un autre traitement (un autre utilisateur sur le web ?)
    Il y a des chances que oui, malheureusement.

    ***
    Je peux te donner quelques idées :

    - Il faut se renseigner sur les verrous (http://dev.mysql.com/doc/refman/5.0/...locks-set.html), voir lequel serait approprié et modifier le code en conséquence

    - Tu peux vérifier si l'utilisateur existe dans la table Users et/ou la table Intervenants en une seule requête, avec une jointure LEFT JOIN:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    SELECT Users.UserID, Intervenants.IntervenantID
    FROM Users
    LEFT OUTER JOIN Intervenants 
    ON ( Users.UserID=Intervenants.IntervenantID)
    WHERE ...
    Si il n'est pas dans Intervenants, alors le champ IntervenantID sera NULL. S'il n'est même pas dans Users, aucune ligne ne sera renvoyée. (Dans l'exemple précédent, j'ai fait l'hypothèse que Intervenant était une "sous-classe" de Users et qu'on remontait la hierarchie de classe par comparaison de clef)

    - l'instruction REPLACE peut être utile: si l'utilisateur existe dans Intervenant, cela change ses données, sinon le REPLACE se comporte comme un INSERT

    - tu peux aussi faire un INSERT IGNORE INTO Intervenants. Si l'intervenant existe déjà, l'erreur sera ignorée

    Voilà, ce sont quelques idées en vrac qui peuvent t'être utiles.

  12. #12
    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
    Tout à l'heure, je me demandais si le fait d'utiliser une TRANSACTION pouvait aider (mais j'étais pas sûr). Il semble que oui: 15.11.1. InnoDB et AUTOCOMMIT

  13. #13
    Rédacteur
    Avatar de Erakis
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Octobre 2003
    Messages
    523
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : Canada

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Octobre 2003
    Messages : 523
    Points : 233
    Points
    233
    Par défaut
    Bonjour,

    Encore un GROS GROS merci !
    J'ai regardé du côté de LOCK TABLE, mais ce n'est pas possible d'utilisation dans les procédures stockés. Ensuite j'ai lu quelque part sur les forums de MySQL que les procédures stockés LOCK en quelque sort les tables qui y sont impliqués alors il serait inutile de les locker soit même. Si je retrouve le liens je vous en fait part.

    Sur ce, je ferme ce post, je crois que j'ai été assez BÉNIS.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [EXCEL] Recuperer un match entre deux colonnes
    Par biggir dans le forum Macros et VBA Excel
    Réponses: 4
    Dernier message: 15/09/2006, 20h43
  2. [Requete] Calcul Somme entre deux temps pour chaque jour
    Par nico33307 dans le forum Requêtes et SQL.
    Réponses: 5
    Dernier message: 21/03/2006, 00h58
  3. Réponses: 2
    Dernier message: 12/01/2006, 10h28
  4. Correspondance entre deux colonnes
    Par AlfiQue dans le forum VB 6 et antérieur
    Réponses: 11
    Dernier message: 09/12/2005, 14h03
  5. Minimum entre deux colonnes
    Par keikun dans le forum Requêtes
    Réponses: 5
    Dernier message: 18/08/2005, 13h20

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