web-dev-qa-db-fra.com

MySQL AUTO_INCREMENT ne fonctionne pas.

J'utilise le champ AUTO_INCREMENT de MySQL et InnoDB pour prendre en charge les transactions. J'ai remarqué que lorsque j'annule la transaction, le champ AUTO_INCREMENT n'est pas annulé? J'ai découvert qu'il a été conçu de cette façon, mais existe-t-il des solutions de contournement? 

56
codegy

Permettez-moi de souligner quelque chose de très important:

Vous ne devriez jamais dépendre des fonctions numériques des clés générées automatiquement.

Autrement dit, à part les comparer pour l'égalité (=) ou l'inégalité (<>), vous ne devriez rien faire d'autre. Pas d'opérateurs relationnels (<,>), pas de tri par index, etc. Si vous avez besoin de trier par "date ajoutée", disposez d'une colonne "date ajoutée".

Traitez-les comme des pommes et des oranges: est-il logique de demander si une pomme est identique à une orange? Oui. Est-il judicieux de demander si une pomme est plus grosse qu'une orange? Non (en fait, si, mais vous voyez ce que je veux dire.)

Si vous vous en tenez à cette règle, les lacunes dans la continuité des index générés automatiquement ne poseront aucun problème.

54
Tamas Czinege

Cela ne peut pas fonctionner comme ça. Considérer:

  • programme un, vous ouvrez une transaction et insérez-la dans une table FOO qui possède une clé primaire autoinc (arbitrairement, nous disons qu’elle obtient 557 comme valeur de clé). 
  • Le programme deux démarre, il ouvre une transaction et l'insère dans la table FOO en obtenant 558. 
  • Le programme deux insère dans la table BAR qui a une colonne qui est une clé étrangère à FOO. Alors maintenant, le 558 est situé à la fois dans FOO et BAR. 
  • Le programme deux s’engage maintenant.
  • Le programme trois commence et génère un rapport à partir de la table FOO. L'enregistrement 558 est imprimé.
  • Après cela, le programme 1 revient en arrière. 

Comment la base de données récupère-t-elle la valeur 557? Va-t-il dans FOO et décrémente toutes les autres clés primaires supérieures à 557? Comment ça règle BAR? Comment efface-t-il les 558 imprimés sur la sortie trois du programme de rapport?

Les numéros de séquence d'Oracle sont également indépendants des transactions pour la même raison.

Si vous pouvez résoudre ce problème en temps constant, je suis sûr que vous pouvez gagner beaucoup d'argent dans le domaine de la base de données.

Maintenant, si vous avez besoin que votre champ d’incrémentation automatique n’ait jamais d’espace vide (à des fins d’audit, par exemple). Ensuite, vous ne pouvez pas annuler vos transactions. Au lieu de cela, vous devez avoir un indicateur de statut sur vos enregistrements. Lors de la première insertion, l'état de l'enregistrement est "Incomplete", puis vous démarrez la transaction, effectuez votre travail et mettez à jour l'état en "concurrente" (ou ce dont vous avez besoin). Ensuite, lorsque vous vous engagez, l'enregistrement est en direct. Si la transaction est annulée, l'enregistrement incomplet est toujours là pour l'audit. Cela vous causera bien d’autres maux de tête, mais c’est un moyen de traiter les pistes de vérification.

69
jmucchiello

Pourquoi vous souciez-vous s'il est annulé? Les champs de clé AUTO_INCREMENT ne sont supposés avoir aucune signification, vous ne devez donc pas vous soucier de la valeur utilisée.

Si vous essayez de conserver des informations, une autre colonne non clé est peut-être nécessaire.

11
Paul Lefebvre

J'ai eu un client avait besoin de l'ID pour annuler sur une table de factures, où la commande doit être consécutive

Ma solution dans MySQL consistait à supprimer AUTO-INCREMENT et à extraire le dernier identifiant de la table, à en ajouter un (+1), puis à l'insérer manuellement.

Si la table est nommée "TableA" et que la colonne Auto-incrémentation est "Id"

INSERT INTO TableA (Id, Col2, Col3, Col4, ...)
VALUES (
(SELECT Id FROM TableA t ORDER BY t.Id DESC LIMIT 1)+1,
Col2_Val, Col3_Val, Col4_Val, ...)
11
Michael Corrigan

Je ne connais aucun moyen de le faire. Selon le Documentation MySQL , il s’agit du comportement attendu et se produira avec tous les modes de verrouillage innodb_autoinc_lock_mode. Le texte spécifique est: 

Dans tous les modes de verrouillage (0, 1 et 2), si un transaction qui a généré Les valeurs d'auto-incrémentation annulent, ces valeurs d'incrémentation automatique sont «Perdu». Une fois qu'une valeur est générée pour une colonne auto-incrémentée, cela ne peut pas être annulé, que le .__ ou non. “INSERT-like” déclaration est terminée, et si le contenant la transaction est annulée. Une telle perdue les valeurs ne sont pas réutilisées. Ainsi, il peut être des lacunes dans les valeurs stockées dans un AUTO_INCREMENT colonne d'une table.

7
gpojd

Si vous définissez auto_increment sur 1 après une restauration ou une suppression, lors de l'insertion suivante, MySQL verra que 1 est déjà utilisé et obtiendra plutôt la valeur MAX() et y ajoutera 1.

Cela garantira que si la ligne avec la dernière valeur est supprimée (ou que l'insertion est annulée), elle sera réutilisée.

Pour définir auto_increment sur 1, procédez comme suit:

ALTER TABLE tbl auto_increment = 1

Ce n'est pas aussi efficace que de continuer simplement avec le numéro suivant car MAX() peut être coûteux, mais si vous supprimez/annulez rarement des opérations et que vous êtes obsédé par la réutilisation de la valeur la plus élevée, il s'agit d'une approche réaliste.

Sachez que cela n'empêche pas les espaces des enregistrements supprimés au milieu, ou si une autre insertion devait se produire avant de redéfinir auto_increment sur 1.

4
Marcus Adams
INSERT INTO prueba(id) 
VALUES (
(SELECT IFNULL( MAX( id ) , 0 )+1 FROM prueba target))

Si la table ne contient pas de valeurs ou zéro ligne

ajouter une cible pour l'erreur mysql type update from FROM on SELECT

2
danis

SOLUTION:

Utilisons 'tbl_test' comme exemple de table et supposons que le champ 'Id' ait l'attribut AUTO_INCREMENT

CREATE TABLE tbl_test (
Id int NOT NULL AUTO_INCREMENT ,
Name varchar(255) NULL ,
PRIMARY KEY (`Id`)
)
;

Supposons que la table a déjà été insérée ou que mille lignes ont déjà été insérées et que vous ne souhaitez plus utiliser AUTO_INCREMENT; parce que lorsque vous annulez une transaction, le champ 'Id' ajoute toujours +1 à la valeur AUTO_INCREMENT . Donc, pour éviter que cela ne se produise:

  • Supprimons la valeur AUTO_INCREMENT de la colonne 'Id' (cela ne supprimera pas les lignes insérées):
ALTER TABLE tbl_test MODIFIER LA COLONNE Id int (11) NOT NULL FIRST;
  • Enfin, nous créons un déclencheur BEFORE INSERT pour générer automatiquement une valeur 'Id'. Mais utiliser cette méthode n’affectera pas votre valeur d’ID même si vous annulez une transaction.
CREATE TRIGGER trg_tbl_test_1 
 AVANT D'INSERER SUR le tbl_test 
 POUR CHAQUE ROW 
 COMMENCER
 SET NEW.Id = COALESCE ((SELECT MAX (Id) FROM tbl_test), 0) + 1; 
 FIN;

C'est tout! Vous avez terminé!

Vous êtes les bienvenus.
1
mld.oscar

La réponse concrète à ce dilemme spécifique (que j’avais aussi) est la suivante:

1) Créez une table contenant différents compteurs pour différents documents (factures, reçus, RMA, etc.); Insérez un enregistrement pour chacun de vos documents et ajoutez le compteur initial à 0.

2) Avant de créer un nouveau document, procédez comme suit (pour les factures, par exemple):

UPDATE document_counters SET counter = LAST_INSERT_ID(counter + 1) where type = 'invoice'

3) Obtenez la dernière valeur que vous venez de mettre à jour, comme ceci:

SELECT LAST_INSERT_ID()

ou utilisez simplement votre fonction PHP (ou autre) mysql_insert_id () pour obtenir la même chose

4) Insérez votre nouvel enregistrement avec l'ID principal que vous venez de récupérer de la base de données. Cela remplacera l'index d'incrémentation automatique actuel et garantira que vous n'avez aucun espace d'identification entre vos enregistrements.

Tout cela doit évidemment être intégré à une transaction. L'avantage de cette méthode est que, lorsque vous annulez une transaction, votre instruction UPDATE de l'étape 2 est annulée et le compteur ne change plus. D'autres transactions simultanées seront bloquées jusqu'à ce que la première transaction soit validée ou annulée, de sorte qu'elles n'auront plus accès à l'ancien compteur OR ni au nouveau, jusqu'à ce que toutes les autres transactions soient terminées en premier.

0
teomor

Si vous avez besoin d'attribuer les identifiants dans un ordre numérique sans espace, vous ne pouvez pas utiliser de colonne auto-incrémentée. Vous devrez définir une colonne d’entier standard et utiliser une procédure stockée qui calcule le nombre suivant dans la séquence d’insertion et insère l’enregistrement dans une transaction. Si l'insertion échoue, la prochaine fois que la procédure sera appelée, elle recalculera l'id suivant.

Cela dit, c’est une mauvaise idée de s’appuyer sur des identifiants qui se trouvent dans un ordre particulier, sans lacunes. Si vous devez conserver l'ordre, vous devez probablement horodater la ligne à l'insertion (et éventuellement à la mise à jour).

0
tvanfosson