2010-06-23 4 views
0

Compte tenu de cette conception de la table dans une application Web:valeur Incrémenter sur la nouvelle ligne

CREATE TABLE `invoice` (
    `invoice_nr` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    `revision` int(10) unsigned NOT NULL DEFAULT '1', 
    PRIMARY KEY (`invoice_nr`,`revision`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_spanish_ci 

Quelle est la façon la plus pratique et fiable pour insérer une nouvelle révision de la facture et récupérer le numéro de révision attribué?

La première approche évidente me paraît peu fiable dans un environnement partagé:

SELECT MAX(revision)+1 AS next_revision -- E.g. 2 
FROM invoice 
WHERE invoice_nr = 31416; 

INSERT INTO invoice (invoice_nr, revision) 
VALUES (31416, 2); 

Cette alternative semble légèrement mieux (je ne sais pas si c'est en fait mieux):

INSERT INTO invoice (invoice_nr, revision) 
SELECT 31416, MAX(revision)+1 
FROM invoice 
WHERE invoice_nr = 31416; 

... mais je ne peux pas connaître le numéro de révision sauf si je lance une nouvelle requête:

SELECT MAX(revision) AS last_revision 
FROM invoice 
WHERE invoice_nr = 31416; 

Existe-t-il une méthode recommandée? L'application fonctionne sur PHP avec la bonne vieille extension de mysql-- mysql_query() et al.

+0

Je ne suis pas sûr de savoir pourquoi vous avez défini la colonne invoice_nr à auto_increment ici lorsque vous insérez plusieurs enregistrements (avec des révisions différentes) contre le même invoice_nr –

+0

@ Baker Baker: Premièrement, parce que les révisions n'existaient pas dans les versions antérieures. Deuxièmement, parce que je pensais que AUTO_INCREMENT remplit toujours son objectif: les nouvelles factures nécessitent un nouveau numéro. –

+0

Vous auriez peut-être intérêt à normaliser une table de factures et une table factice_revisions –

Répondre

0

J'ai rassemblé quelques techniques du manuel MySQL. Je pensais que je devrais les partager ici pour les disques.

1. Le verrouillage de table

Si vous placez un verrou sur la table, d'autres processus simultanés seront mis en attente., Vous n'avez pas besoin alors une astuce pour éviter dupes:

LOCK TABLES invoice WRITE; 

SELECT MAX(revision)+1 AS next_revision -- E.g. 2 
FROM invoice 
WHERE invoice_nr = 31416; 

INSERT INTO invoice (invoice_nr, revision) 
VALUES (31416, 2); 

-- Or INSERT INTO ... SELECT 

UNLOCK TABLES; 

2. LAST_INSERT_ID (expr)

La fonction LAST_INSERT_ID() accepte un paramètre optionnel. Si elle est définie, elle renvoie une telle valeur et la stocke également pour la session en cours, de sorte que l'appel suivant à LAST_INSERT_ID() renvoie cette valeur. Ceci est un moyen pratique d'imiter des séquences:

CREATE TABLE `sequence` (
    `last_value` INT(50) UNSIGNED NOT NULL DEFAULT '1' COMMENT 'Last value used' 
); 

START TRANSACTION; 

UPDATE sequence 
SET last_value=LAST_INSERT_ID(last_value+1); 

INSERT INTO invoice (invoice_nr, revision) 
VALUES (31416, LAST_INSERT_ID()); 

COMMIT; 
3

Mysql a une fonction appelée last_insert_id() qui renvoie le dernier ID généré automatiquement. Donc, vous pouvez juste SELECT last_insert_id() directement après l'insertion de vos données.

PHP a une fonction intégrée pour ce faire appelé mysql_insert_id().

Plus sur cette ici: http://www.php.net/manual/en/function.mysql-insert-id.php

Alors que tout est vrai et généralement utile, il est pas en fait ce qui est recherché ici. Comment je ferais ceci est générer aux tables. Une facture appelée avec votre champ d'incrémentation automatique et une seconde appelée facture_revisions. Cela devrait avoir le même format que la table de facture avec le champ de vision ajouté.

Ensuite, lorsque vous mettez à jour votre table de facture vous devez d'abord faire:

INSERT INTO invoice_revision SELECT i.*,IFNULL(max(ir.revision),0)+1 AS revision FROM invoice i LEFT JOIN invoice_revision ir on ir.invoice_nr = i.invoice_nr WHERE i.invoice_nr = ?

puis mettre à jour votre table de facture normale. De cette façon, vous avez vos données à jour dans la table des factures et la liste de toutes les versions précédentes dans la table invoice_revisions.

Remarque si vous utilisez des tables MyISAM vous et définir la révision à la auto_increment dans ce tableau:

http://dev.mysql.com/doc/refman/5.5/en/example-auto-increment.html

+0

Suggérez-vous d'ajouter une nouvelle colonne d'identifiant et de retirer 'invoice_nr' et' revision' de la clé primaire? –

+0

J'ai mis à jour ma réponse pour donner une solution plus détaillée au problème. – Scimon

0

si vous mettez une transaction correctement isolé autour de votre insert et sélectionnez personne ne peut modifier ces tables entre les deux déclarations.

Une autre approche consiste à utiliser une procédure stockée pour exécuter les deux commandes et renvoyer le résultat.

+0

Je ne suis pas encore très familier avec les transactions MySQL. Sont-ils sûrs à cet effet? –

+0

J'ai fait d'autres tests. Les transactions ne semblent pas être le bon outil: vous avez probablement pensé au verrouillage de table. L'émission d'une requête LOCK TABLES facture WRITE suivie de SELECT MAX (révision) +1 ... et de INSERT INTO ... apparaît pour empêcher les dupes dues à un accès concurrent. –

Questions connexes