web-dev-qa-db-fra.com

MySQL UPDATE et SELECT en un seul passage

J'ai une table de tâches MySQL à effectuer, chaque ligne ayant des paramètres pour une tâche unique.
Il existe de nombreuses applications de travail (éventuellement sur différentes machines) effectuant des tâches en boucle.
Les applications accèdent à la base de données à l’aide des API C natives de MySQL.

Afin de posséder une tâche, une application fait quelque chose comme ça: 

  • Générer un identifiant unique au monde (pour simplifier, disons que c'est un nombre) 

  • UPDATE tasks
    SET guid = %d
    WHERE guid = 0 LIMIT 1

  • SELECT params
    FROM tasks
    WHERE guid = %d 

  • Si la dernière requête retourne une ligne, nous la possédons et avons les paramètres à exécuter 

Existe-t-il un moyen d'obtenir le même effet (c'est-à-dire "posséder" une ligne et obtenir ses paramètres) en un seul appel au serveur?

18
Paul Oyster

essayez comme ça

UPDATE `lastid` SET `idnum` =  (SELECT `id` FROM `history` ORDER BY `id` DESC LIMIT 1);

le code ci-dessus a fonctionné pour moi

9
charles

Vous pouvez créer une procédure qui le fait:

CREATE PROCEDURE prc_get_task (in_guid BINARY(16), OUT out_params VARCHAR(200))
BEGIN

  DECLARE task_id INT;

  SELECT id, out_params
  INTO task_id, out_params
  FROM tasks
  WHERE guid = 0
  LIMIT 1
  FOR UPDATE;

  UPDATE task
  SET guid = in_guid
  WHERE id = task_id;

END;

BEGIN TRANSACTION;

CALL prc_get_task(@guid, @params);

COMMIT;
6
Quassnoi

J'ai exactement le même problème. Nous avons fini par utiliser PostreSQL à la place, et UPDATE ... RETURNING :

La clause optionnelle RETURNING permet à UPDATE de calculer et de renvoyer des valeurs en fonction de chaque ligne réellement mise à jour. Toute expression utilisant les colonnes de la table et/ou les colonnes d'autres tables mentionnées dans FROM peut être calculée. Les nouvelles valeurs (postérieures à la mise à jour) des colonnes de la table sont utilisées. La syntaxe de la liste RETURNING est identique à celle de la liste de sortie de SELECT.

Exemple: UPDATE 'my_table' SET 'status' = 1 WHERE 'status' = 0 LIMIT 1 RETURNING *;

Ou, dans votre cas: UPDATE 'tasks' SET 'guid' = %d WHERE 'guid' = 0 LIMIT 1 RETURNING 'params';

Désolé, je sais que cela ne répond pas à la question posée avec MySQL et qu'il n'est peut-être pas facile de passer à PostgreSQL, mais c'est la meilleure façon de le faire. Même 6 ans plus tard, MySQL ne supporte toujours pas UPDATE ... RETURNING. Il sera peut-être ajouté ultérieurement, mais pour le moment MariaDB ne l’a que pour les instructions DELETE .

Edit: Il existe une tâche (priorité basse) pour ajouter le support UPDATE ... RETURNING à MariaDB .

1
Marco Roy

Si vous recherchez une seule requête, cela ne peut pas arriver. La fonction UPDATE renvoie spécifiquement le nombre d'éléments mis à jour. De même, la fonction SELECT ne modifie pas une table, elle ne renvoie que des valeurs.

En utilisant une procédure, vous la transformerez en une seule fonction. Cela peut être pratique si le verrouillage vous préoccupe. Si votre principale préoccupation est le trafic réseau (c'est-à-dire: transmettre trop de requêtes), utilisez la procédure. Si vous vous préoccupez de la surcharge du serveur (c'est-à-dire que la base de données fonctionne trop dur), la surcharge supplémentaire d'une procédure pourrait aggraver les choses.

1
Paulo

Je ne sais pas pour la partie appel unique, mais ce que vous décrivez est un verrou. Les serrures sont un élément essentiel des bases de données relationnelles.

Je ne connais pas les spécificités de verrouiller une ligne, de la lire, puis de la mettre à jour dans MySQL, mais avec un peu de lecture de la documentation sur le verrou mysql , vous pouvez effectuer toutes sortes de manipulations basées sur le verrouillage.

La documentation postgres de locks présente un excellent exemple décrivant exactement ce que vous voulez faire: verrouiller la table, lire la table, modifier la table.

0
Daniel
UPDATE tasks
SET guid = %d, params = @params := params
WHERE guid = 0 LIMIT 1;

Il retournera 1 ou 0, selon que les valeurs ont effectivement été modifiées.

SELECT @params AS params;

Celui-ci ne fait que sélectionner la variable de la connexion.

De: here

0
jogaco