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

 MySQL Discussion :

Gérer auto_increment manuellement


Sujet :

MySQL

  1. #1
    Membre régulier
    Profil pro
    Inscrit en
    Novembre 2007
    Messages
    901
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2007
    Messages : 901
    Points : 79
    Points
    79
    Par défaut Gérer auto_increment manuellement
    Bonjour,

    J'aurais une autre petite question concernant une table de facturation.
    Dans cette table je sais que je suis censé utiliser une colonne Auto_Increment pour les numéros de factures. Sauf que cette table contiendra les factures de 2 pays différents. Voici un exemple simplifié de la table :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    sequence | numero
    01       | 10001
    01       | 10002
    01       | 10003
    02       | 10001
    02       | 10002
    01       | 10004
    ...
    Est-ce que déjà cette solution est bien ? Ou faut-il créer 2 tables, une pour chaque pays ?

    Merci

  2. #2
    Membre régulier
    Profil pro
    Inscrit en
    Novembre 2007
    Messages
    901
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2007
    Messages : 901
    Points : 79
    Points
    79
    Par défaut
    Une petite précision, j'ai oublié de mentionner que j'utilise le type InnoDB.

  3. #3
    Expert confirmé
    Avatar de TiranusKBX
    Homme Profil pro
    Développeur C, C++, C#, Python, PHP, HTML, JS, Laravel, Vue.js
    Inscrit en
    Avril 2013
    Messages
    1 476
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Seine et Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur C, C++, C#, Python, PHP, HTML, JS, Laravel, Vue.js
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2013
    Messages : 1 476
    Points : 4 805
    Points
    4 805
    Billets dans le blog
    6
    Par défaut
    tout dépend si il est absolument nécessaire que les numéros soient en fonction du pays ou uniques avec une différenciation par pays
    Rien, je n'ai plus rien de pertinent à ajouter

  4. #4
    Membre régulier
    Profil pro
    Inscrit en
    Novembre 2007
    Messages
    901
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2007
    Messages : 901
    Points : 79
    Points
    79
    Par défaut
    Bonjour,

    Il est absolument nécessaire que les numéros soient en fonction du pays

    Ex de facture pour la france: F01-100001, F01-100002, F01-100003, F01-100004
    et pour la belgique: F02-100001, F02-100002, F02-100003, F02-100004

  5. #5
    Expert confirmé
    Avatar de TiranusKBX
    Homme Profil pro
    Développeur C, C++, C#, Python, PHP, HTML, JS, Laravel, Vue.js
    Inscrit en
    Avril 2013
    Messages
    1 476
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France, Seine et Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur C, C++, C#, Python, PHP, HTML, JS, Laravel, Vue.js
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Avril 2013
    Messages : 1 476
    Points : 4 805
    Points
    4 805
    Billets dans le blog
    6
    Par défaut
    alors je verrais :
    ID(id absolus de facture dans la table)
    pays(id pays)
    numero(id facture)
    data,
    ....

    Mais pour gérer les numéros il te faudra dans ton code appelant récupérer à chaque fois le dernier id facture du pays concerné avant d'en générer une nouvelle.
    C'est le seul moyen de le faire ou alors il de faut passer par les procédures stockées.
    Rien, je n'ai plus rien de pertinent à ajouter

  6. #6
    Membre régulier
    Profil pro
    Inscrit en
    Novembre 2007
    Messages
    901
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2007
    Messages : 901
    Points : 79
    Points
    79
    Par défaut
    Oui, j'ai fait quelque chose du genre. voici mon code si cela peut interesser quelqu'un:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    $id_order = 10001;
    $invoice_sequence = 01;
    $ins = $dbh->prepare("INSERT INTO invoices (id_order, invoice_type, invoice_sequence, number)
    	SELECT ?, ?, ?, IFNULL(MAX(number) + 1, 100001) FROM invoices WHERE invoice_sequence = ?");
     
    do { // au cas ou 2 facture se créent a la meme micro seconde
    	if($ins->execute(array($id_order, "F", $invoice_sequence, $invoice_sequence))) break;
    } while(0);

  7. #7
    Expert confirmé
    Homme Profil pro
    Inscrit en
    Mai 2002
    Messages
    3 173
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Rhône (Rhône Alpes)

    Informations forums :
    Inscription : Mai 2002
    Messages : 3 173
    Points : 5 345
    Points
    5 345
    Par défaut
    Gérez vos séquences dans une table à part.

    Créez une procédure qui retournera le max+1, tout en gérant correctement le niveau de transaction (serializable si ça marche sous mysql).
    C'est le seul moyen de vous prémunir des doublons http://sqlpro.developpez.com/cours/clefs/#L3.2 (un exemple à adapter).


    N'hésitez pas sinon à rajouter un index unique sur vos 2 colonnes...

  8. #8
    Membre régulier
    Profil pro
    Inscrit en
    Novembre 2007
    Messages
    901
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2007
    Messages : 901
    Points : 79
    Points
    79
    Par défaut
    Désolé, je n'ai pas été très précis dans mes précédents posts.
    Effectivement, je gère déjà les séquences dans une autre table, dans laquelle la colonne invoice_sequence est une PK UNSIGNED ZEROFILL.

    Puis dans la table factures, j'ai mis PK sur les 2 colonnes: number et invoice_sequence

  9. #9
    Membre expérimenté
    Homme Profil pro
    Développeur C++
    Inscrit en
    Avril 2012
    Messages
    771
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur C++
    Secteur : Industrie

    Informations forums :
    Inscription : Avril 2012
    Messages : 771
    Points : 1 631
    Points
    1 631
    Par défaut
    Bonjour,

    il ne faut pas faire :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    SELECT MAX(id)
    FROM maTable
    Car cette requête si elle est exécutée au même moment par deux clients, deux facture auront le même ID.
    Pour contrer ça SELECT .. FOR UPDATE
    une réponse vous a permis d'avancer ?

  10. #10
    Membre régulier
    Profil pro
    Inscrit en
    Novembre 2007
    Messages
    901
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2007
    Messages : 901
    Points : 79
    Points
    79
    Par défaut
    Bonsoir Exia93,

    En fait, j'avais déjà essaye avec SELECT ... FOR UPDATE mais MySQL renvoie une erreur:
    #1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'LIMIT 1' at line 1
    Je pense que c'est parce que j'utilise innoDB.

    Mais avec ce que j'ai mis, on ne peut pas avoir le même ID puisque ID, sequence présentent une clef primaire. Et avec la boucle do while, il ré-exécutera la requête sql s'il y a un doublon au cas où 2 clients exécutent la requête en même temps...

  11. #11
    Membre expérimenté
    Homme Profil pro
    Développeur C++
    Inscrit en
    Avril 2012
    Messages
    771
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur C++
    Secteur : Industrie

    Informations forums :
    Inscription : Avril 2012
    Messages : 771
    Points : 1 631
    Points
    1 631
    Par défaut
    Le SELECT .. FOR UPDATE est utilisable seulement sur les moteurs supportant les transactions, ce que fait parfaitement InnoDB.
    Quelle était votre requête avec le SELECT ... FOR UPDATE ? Il est possible que vous ayez simplement fait une erreur de syntaxe.

    Je ne comprends pas l'utilité de votre boucle, vu que vous ne modifiez aucune des variables de la requête à chaque tour de boucle...
    une réponse vous a permis d'avancer ?

  12. #12
    Membre régulier
    Profil pro
    Inscrit en
    Novembre 2007
    Messages
    901
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2007
    Messages : 901
    Points : 79
    Points
    79
    Par défaut
    Bonjour,

    j'ai fait un select tout simple de cette facon:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SELECT number FROM invoices FOR UPDATE;

  13. #13
    Membre expérimenté
    Homme Profil pro
    Développeur C++
    Inscrit en
    Avril 2012
    Messages
    771
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur C++
    Secteur : Industrie

    Informations forums :
    Inscription : Avril 2012
    Messages : 771
    Points : 1 631
    Points
    1 631
    Par défaut
    Dans l'erreur il est question d'un LIMIT 1 alors que dans l'exemple il n'y a pas cette clause...
    une réponse vous a permis d'avancer ?

  14. #14
    Membre régulier
    Profil pro
    Inscrit en
    Novembre 2007
    Messages
    901
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2007
    Messages : 901
    Points : 79
    Points
    79
    Par défaut
    Désolé, j'avais juste omis de le mettre. Voici la requête que j'ai mise :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SELECT number FROM invoices FOR UPDATE LIMIT 1

  15. #15
    Membre expérimenté
    Homme Profil pro
    Développeur C++
    Inscrit en
    Avril 2012
    Messages
    771
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur C++
    Secteur : Industrie

    Informations forums :
    Inscription : Avril 2012
    Messages : 771
    Points : 1 631
    Points
    1 631
    Par défaut
    Aviez vous essayé :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SELECT number FROM invoices LIMIT 1 FOR UPDATE
    Car il me semble que la clause FOR UPDATE doit être mise à la fin de la requête.

    Maintenant j'imagine que votre requête finale ne ressemblera pas à celle-là, car le LIMIT 1 ne garantit pas le dernier enregistrement inséré. Avec la clause FOR UPDATE, faire un :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SELECT MAX(number) FROM invoices FOR UPDATE
    est alors possible.
    une réponse vous a permis d'avancer ?

  16. #16
    Membre régulier
    Profil pro
    Inscrit en
    Novembre 2007
    Messages
    901
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2007
    Messages : 901
    Points : 79
    Points
    79
    Par défaut
    Car il me semble que la clause FOR UPDATE doit être mise à la fin de la requête.
    Ah ouaissss jamais je n'aurais pensé à mettre la clause FOR UPDATE à la fin
    Effectivement ça marche bien et bien évidemment, je mettrai le MAX(number)

    Mais sinon pour mieux comprendre le SELECT ... FOR UPDATE, qu'est ce qui se passe concrètement après l’exécution de cette requête ?

  17. #17
    Membre expérimenté
    Homme Profil pro
    Développeur C++
    Inscrit en
    Avril 2012
    Messages
    771
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur C++
    Secteur : Industrie

    Informations forums :
    Inscription : Avril 2012
    Messages : 771
    Points : 1 631
    Points
    1 631
    Par défaut
    La clause FOR UPDATE permet de poser un verrou sur la ligne (ou la table je ne me souviens plus ^^') qui est retourné par la requête. Si d'autres clients essaient d'accéder à la ligne (ou table) InnoDB les fera patienter jusqu'à la fin de la transaction, ce qui permet d'être assuré que la donnée n'est modifiée que par un seul client à la fois.

    Il est même possible au lieu de faire un MAX d'utiliser une table de séquence.
    Elle serait composée de 3 colonnes :
    nomTable : varChar(255)
    nomSequence : varChar(255)
    value : BIGINT

    Ensuite lorsqu'une nouvelle table est créée, il suffit d'ajouter un enregistrement dans la table sequence pour chaque colonne ayant une colonne avec le même comportement que l'auto_increment.
    Ensuite lorsque l'on veut ajouter un nouvel enregistrement dans une table, par exemple un nouvel invoices dans votre cas. On récupère à l'aide d'un SELECT ... FOR UPDATE l'id de cette colonne dans la table sequence, on crée le nouvel enregistrement dans la table invoices puis on ajoute 1 à la colonne dans la table sequence.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    -- exemple de code
    int lastId = db.query("SELECT value FROM sequence WHERE nomTable = 'invoices' AND nomSequence = 'number' FOR UPDATE");
    lastId = lastId + 1;
    db.query("INSERT INTO invoices VALUES(lastId, 'toto', 'titi', 'tutu')");
    db.query("UPDATE sequence set value = value +1 WHERE nomTable = 'invoices' AND nomSequence = 'number'");
    Il me semble que le fait de faire un UPDATE sur la table sequence valide la transaction et permet aux autres clients d'accéder à l'information, mais c'est à vérifier.
    une réponse vous a permis d'avancer ?

  18. #18
    Membre régulier
    Profil pro
    Inscrit en
    Novembre 2007
    Messages
    901
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2007
    Messages : 901
    Points : 79
    Points
    79
    Par défaut
    Donc en gros, un SELECT max(colonne) ... FOR UPDATE bloquera toute autre requêtes sur cette même table, c'est bien ça ?
    Mais s'il y a disons une dizaine de requêtes sur cette table en même temps, est-ce que ça risque de créer un conflit avec ces requêtes "différées" ?

    Je ne comprends pas l'utilité de votre boucle vu que vous ne modifiez aucune des variables de la requête à chaque tour de boucle
    En fait, je n'ai pas besoin de modifier les variables, puisque si la 1ere exécution échoue, alors il va ré-exécuter la requête en récupérant la nouvelle valeur Max(number) puis ajoute + 1...
    J'espère que vous voyez mieux ce que je veux dire.

  19. #19
    Membre expérimenté
    Homme Profil pro
    Développeur C++
    Inscrit en
    Avril 2012
    Messages
    771
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur C++
    Secteur : Industrie

    Informations forums :
    Inscription : Avril 2012
    Messages : 771
    Points : 1 631
    Points
    1 631
    Par défaut
    Comme dit plus haut, je ne me souviens pas si la requête bloque la ligne spécifique ou la table entière (sachant que avec la table séquence je suis sûr que ce n'est que la ligne qui est lockée, maintenant avec un MAX vu que le MAX est dépendant de la table je ne sais pas).

    Cependant, pour réduire le temps de lock de la table (ou ligne), il est préférable d'utiliser une procédure stockée qui reçoit en paramètres toutes les informations à l'insertion d'une nouvelle ligne dans votre table invoices, va utiliser le SELECT ... FOR UPDATE, insérer la ligne et faire la mise à jour.

    Étant seulement côté SGBD pour faire c'est 3 traitements, le temps de lock sera beaucoup moins long, car il n'y a qu'un seul envoi sur le réseaux.
    une réponse vous a permis d'avancer ?

  20. #20
    Membre régulier
    Profil pro
    Inscrit en
    Novembre 2007
    Messages
    901
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2007
    Messages : 901
    Points : 79
    Points
    79
    Par défaut
    OK merci. Je vais regarder cela de plus près... mais bon, ça me fait un peu peur, cette histoire de lock...

Discussions similaires

  1. Gérer un autocomplete de combobox manuellement
    Par 2nd Floor dans le forum Débuter
    Réponses: 14
    Dernier message: 06/10/2011, 01h18
  2. gérer manuellement la barre de défilement
    Par mouad83 dans le forum Forms
    Réponses: 14
    Dernier message: 09/01/2009, 17h27
  3. gérer manuellement le scroll d'un flowlayoutpanel ?
    Par cf1109 dans le forum Windows Forms
    Réponses: 2
    Dernier message: 12/11/2007, 20h04
  4. [MySQL] Comment gérer les auto_increment ?
    Par Tr@nkill dans le forum PHP & Base de données
    Réponses: 1
    Dernier message: 02/05/2006, 10h07
  5. gérer les jpg dans une fenetre directdraw???
    Par Anonymous dans le forum DirectX
    Réponses: 1
    Dernier message: 14/06/2002, 13h39

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