2014-08-28 13 views
1

Je suis dans une situation de concurrence parce que je fais face à beaucoup de concurrence. J'essaie de combiner ces deux instructions mysql pour les exécuter en même temps.SELECT & UPDATE en même temps - condition de course

J'ai besoin de sélectionner une ligne et mettre à jour le même ...

SELECT id_file FROM filenames WHERE pending=1 LIMIT 1; 
UPDATE filenames SET pending=2 WHERE id_file=**id of select query**; 

Une autre solution à la condition de course je rencontre serait d'effectuer une requête UPDATE où l'attente = 1 et obtenir en quelque sorte l'ID de la ligne mise à jour, mais je ne suis pas sûr si c'est même possible?

Merci

+0

UPDATE nom de fichier SET pending = 2 WHERE pending = 1 LIMIT 1 – cornelb

+0

Désolé, je dois spécifier, j'ai toujours besoin de l'ID/résultat de l'instruction SELECT dans l'application. – user2980769

+0

Vous n'avez pas déjà le 'id_file' si vous exécutez l'instruction' UPDATE'? – user145400

Répondre

1

Vous pouvez éviter la condition de « race » en effectuant une déclaration UPDATE juste sur la table, permettent d'identifier que la ligne à modifier, puis récupérer ensuite les valeurs de colonnes de la ligne.

Il y a un "trick" retournant des valeurs de colonnes, dans votre cas, la valeur de la colonne id_file de la ligne qui vient d'être mise à jour. Vous pouvez utiliser la fonction LAST_INSERT_ID() (uniquement si la colonne est de type entier) ou une variable MySQL définie par l'utilisateur.

Si la valeur de la colonne que vous souhaitez récupérer est un nombre entier, vous pouvez utiliser la fonction LAST_INSERT_ID() (qui prend en charge une valeur BIGINT-64).

Par exemple:

UPDATE filenames 
    SET pending = 2 
    , id_file = LAST_INSERT_ID(id_file) 
WHERE pending = 1 
LIMIT 1; 

Après l'exécution réussie de l'instruction UPDATE, vous aurez envie de vérifier que au moins une ligne a été affectée. . (Si toutes les lignes remplies WHERE, et l'instruction a abouti, nous savons qu'une ligne sera affectée Ensuite, vous pouvez récupérer cette valeur, dans la même session:

SELECT LAST_INSERT_ID(); 

pour récupérer la valeur de id_file colonne du Notez que si le UPDATE traite plusieurs lignes, seule la valeur dernière ligne qui a été traitée par UPDATE sera disponible. LIMIT 1 clause.)

Encore une fois, vous voulez vous assurer qu'une ligne a été réellement mise à jour, avant de vous fier à la valeur retournée par le LAST_INSERT_ID() fonction.


Pour les colonnes non entières, vous pouvez utiliser une variable définie par l'utilisateur MySQL de la même manière, affecter la valeur de la colonne à une variable définie par l'utilisateur, puis récupérer immédiatement la valeur stockée dans l'utilisateur variable définie

-- initialize user-defined variable, to "clear" any previous value 
SELECT @id_file := NULL; 

-- save value of id_file column into user-defined variable 
UPDATE filenames 
    SET pending = 2 
    , id_file = (SELECT @id_file := id_file) 
WHERE pending = 1 
LIMIT 1; 

-- retrieve value stored in user-defined variable 
SELECT @id_file; 

Notez que la valeur de cette variable est conservée dans la session. Si l'instruction UPDATE ne trouve aucune ligne satisfaisant le prédicat (clause WHERE), la valeur de la variable définie par l'utilisateur ne sera pas affectée ... donc, pour vous assurer de ne pas avoir par inadvertance une valeur "ancienne", vous pouvez vouloir initialiser cette variable avec une valeur NULL.

Notez qu'il est important qu'un déclencheur déclenché par la suite ne modifie pas la valeur de cette variable définie par l'utilisateur. (La variable définie par l'utilisateur est "dans la portée" dans la session en cours.

Il est également possible de faire l'affectation à la variable définie par l'utilisateur dans un déclencheur, mais je ne vais pas le démontrer, et je ne recommanderais pas de le faire dans un déclencheur.

1

Pour faire face à la concurrence est l'une des fonctions de base de transactions.

Enveloppez vos requêtes en une seule opération et dire le SGBD, que vous avez besoin de la ligne de ne pas changer entre les deux avec FOR UPDATE:

BEGIN; 
SELECT id_file FROM filenames WHERE pending=1 LIMIT 1 FOR UPDATE; 
# do whatever you like 
UPDATE filenames SET pending=2 WHERE id_file=**id of select query**; 
COMMIT; 

Vous pouvez exécuter ces déclarations avec 4 mysqli_query appels, et faire ce que vous voulez entre les deux, sans avoir à vous soucier de la cohérence de votre base de données. La ligne sélectionnée est enregistrée jusqu'à ce que vous la relâchiez.