Je souhaite effectuer une mise à jour uniquement si l'instruction select renvoie une valeur particulière.
Exemple:
Table donnée:
ItemId | CheckIn 0 0 1 0 2 1 3 1 4 1
J'exploiterais une grande partie des ItemIds, par exemple ItemId 1,2,3.
Si TOUS les ItemIds sélectionnés ont la valeur CheckIn 0, alors je devrais mettre à jour CheckIn des ItemIds à 1. Sinon, si à au moins l'un des ItemIds sélectionnés est égal à 1, aucune mise à jour ne doit être effectuée.
Est-ce possible dans une seule instruction SELECT / UPDATE du tout? strong>
La vérification de la valeur CheckIn et la mise à jour qui suit doivent être thread-safe (Java 8).
Ce qui fonctionnerait, c'est un SELECT FOR UPDATE séparé avant l'instruction UPDATE réelle , ce que je veux éviter cependant.
En tant que tel, la seule autre option que je pense serait de placer le SELECT avec UPDATE dans une seule requête conditionnelle, et je ne suis pas sûr que cela soit possible, d'où ma question.
Je travaille avec Java 8 btw, donc une solution basée sur Java serait également acceptable si vous savez comment pour y parvenir.
3 Réponses :
Vous pouvez procéder de cette manière:
Requête MySQL:
UPDATE temp_table SET CheckIn = 1 WHERE ItemId IN (1, 2, 3) AND 0 = (SELECT sum(derived_table.CheckIn) FROM (SELECT CheckIn FROM temp_table WHERE ItemId IN (1, 2, 3)) derived_table);
Requête Oracle:
UPDATE temp_table SET CheckIn = 1 WHERE ItemId IN (1, 2, 3) AND (SELECT sum(derived_table.CheckIn) = 0 FROM (SELECT CheckIn FROM temp_table WHERE ItemId IN (1, 2, 3)) AS derived_table);
J'ai créé une table dérivée et utilisé une fonction de groupe sum ()
pour gérer ce scénario. Je l'ai testé et il fonctionne comme décrit dans la question.
Essayez ceci:
UPDATE YOUR_TABLE TOUT SET TOUT.CHECKIN = ( SELECT CASE WHEN SUM(T.CHECKIN) = 0 THEN 1 ELSE TOUT.CHECKIN END FROM YOUR_TABLE T WHERE T.ITEMID IN ( 1, 2, 3 ) ) WHERE TOUT.ITEMID IN ( 1, 2, 3 );
Cheers !!
Cette requête est pire que la version avec sum ()
dans la clause where
car elle peut générer beaucoup plus d'annulation. Mettre à jour une valeur pour elle-même n'est pas la même chose que ne pas mettre à jour la valeur du tout.
Dans la clause WHERE, il n'y a que 3 éléments. Je ne pense pas que cela générera beaucoup de données d'annulation.
Bien, dans ce cas, ce ne sera pas le cas (à moins que la requête ne soit exécutée, disons 1 000 000 fois), mais ce n'est jamais une bonne pratique quel que soit le nombre d'éléments. De plus, s'il y a une nouvelle exigence pour supprimer le filtre par élément, votre requête doit être complètement refactorisée.
Bien sûr, vous avez raison, mais dans ce cas, je ne pense pas que nous devrions même penser à annuler
Vous pouvez le faire dans une instruction de fusion, par exemple:
MERGE INTO your_table tgt USING (SELECT itemid, checkin, overall_checkin FROM (SELECT itemid, checkin, MAX(CASE WHEN checkin = 0 THEN 0 ELSE 1) OVER () overall_checkin FROM your_table WHERE itemid IN (1, 2, 3)) WHERE overall_checkin = 0) src ON (tgt.itemid = src.itemid) -- assuming that itemid is a primary/unique column WHEN MATCHED THEN UPDATE tgt.checkin = 1;
Et voici un db fiddle démontrant l'instruction de fusion.
Pourquoi faire des hypothèses si vous pouvez fusionner sur rowid?
Pourquoi n'utilisez-vous pas l'instruction select comme sous-requête? Quelque chose comme ceci:
update some_table set some_field = some_value where (select some_field = some_value from some_table where some_field in (some_list))
Je n'ai pas besoin d'une mise à jour conditionnelle dans laquelle je ne mets à jour que les lignes d'une certaine valeur, j'ai besoin d'une mise à jour conditionnelle dans laquelle je n'effectue une mise à jour que si la sous-requête renvoie un certain résultat. En tant que tel, je souhaite effectuer une mise à jour uniquement si AUCUNE des valeurs CheckIn des ItemIds sélectionnés n'a la valeur 1.
Dans ce cas, vous souhaiterez peut-être stocker le résultat de la requête
select
dans une variable. Et puis exécutez la requêteupdate
basée sur ce résultat.Ce n'est pas thread-safe, comme décrit dans la question d'origine. Et je veux éviter de verrouiller les lignes, comme décrit