0
votes

Mettre à jour un enregistrement s'il existe, sinon ne rien faire, y compris short-cicruit

Ce que je veux faire, c'est quelque chose qui a la logique suivante:

#4078 - Illegal parameter data types varchar and row for operation '='

.., donc pour mettre à jour un champ d'un enregistrement donné si cet enregistrement contient une donnée donnée, et sinon ne rien faire. Pour générer les données qui seraient utilisées pour la mise à jour, je dois interroger d'autres tables pour les valeurs et faire quelques calculs. Je veux éviter ces requêtes + calculs, s'il n'y a en fait rien à mettre à jour. Et dans ce cas, ne faites simplement rien. Par conséquent, je suppose que mettre par exemple une clause EXIST dans une clause WHERE de l'instruction UPDATE se terminerait par de nombreuses requêtes et calculs effectués en vain.

Comment puis-je MISE À JOUR uniquement de manière conditionnelle et sinon ne rien faire, et m'assurer que toutes les requêtes + calculs nécessaires pour calculer la valeur utilisée pour la mise à jour ne sont effectués que si la mise à jour est nécessaire? Et puis, à la fin, ne faire la mise à jour que si complex_queries_and_calculations n'est pas NULL?

Ma meilleure solution jusqu'à présent utilise une expression de table commune (clause WITH ), ce qui rend impossible le court-circuit. Quoi qu'il en soit, pour que vous puissiez comprendre la logique que j'essaie de réaliser, je montre ce que j'ai essayé jusqu'à présent (sans succès; le code ci-dessous ne fonctionne pas et je ne sais pas pourquoi ..):

UPDATE people AS p
INNER JOIN (queries_and_calculations AS result) t
ON p.ID <> t.result -- just to join
SET p.target_column = t.result
WHERE p.ID = 168
AND t.result IS NOT NULL;

MISES À JOUR (toujours en train de dire une erreur de syntaxe, ne fonctionne toujours pas)

WITH t AS(complex_queries_and_calculations AS stamp)
SELECT
CASE
WHEN t.stamp IS NULL
THEN "Error!"
ELSE t.stamp
END
FROM t;

Même cela ne fonctionne pas (signalant toujours une erreur de syntaxe sur la ligne UPDATE):

WITH t AS(complex_queries_and_calculations AS stamp)
UPDATE target_table
SET target_column = target_column
WHERE ID = 168;

(les meilleures approches éventuelles qui évitent les mises à jour redondantes de target_column = target_column sont les bienvenues)

Avec select, cela fonctionne, donc je ne comprends totalement pas l'erreur de syntaxe # 1064 qu'elle renvoie pour ma requête de mise à jour:

WITH t AS(complex_queries_and_calculations AS stamp)
UPDATE target_table
SET target_column =
CASE
WHEN t.stamp IS NULL
THEN target_column
ELSE t.stamp
END
WHERE ID = 168;

INFORMATION ADDITIONNELLE

Il semble que MariaDB ne supporte pas les CTE avec des instructions UPDATE ; corrigez-moi si je me trompe ... J'ai donc essayé ce qui suit:

-- complex queries and calculations; return result as t.result
WITH t AS(complex queries and calculations)
UPDATE target_table
SET
CASE
WHEN t.result IS NOT NULL
THEN target_table.target_column = t.result WHERE target_table.PK = 180
END;

et maintenant il dit:

IF EXISTS (SELECT * FROM people WHERE ID = 168)
THEN UPDATE people SET calculated_value = complex_queries_and_calculations
WHERE ID = 168


2 commentaires

vous devez fournir l'ensemble de la requête avec des exemples de données pour que la communauté puisse vous aider.


À partir du mot-clé de mise à jour, les requêtes sont exactement telles qu'elles sont, veuillez vous concentrer sur cette partie / comment le faire fonctionner


3 Réponses :


3
votes

Faites simplement la UPDATE . S'il n'y a pas de ligne avec cet ID , cela ne fera rien. Ce ne sera probablement pas plus lent que de tester d'abord.

Idem pour DELETE lorsque la ligne peut ne pas exister.

"Upsert" / "IODKU" - INSERT ... ON DUPLICATE KEY UPDATE ... est utile lorsque vous souhaitez modifier certaines colonnes lorsque la ligne existe (selon une colonne unique), ou ajouter une nouvelle ligne (quand c'est le cas n'existe pas). C'est mieux que de faire d'abord un SELECT .

Pensez-y de cette façon ... Une grande partie de la UPDATE est

  • ouvrir la table,
  • localiser le bloc dans la table à modifier
  • chargement de ce bloc dans le cache ("buffer_pool")

Tout cela est nécessaire pour votre SELECT et UPDATE (ouais, de manière redondante). La UPDATE poursuit avec:

  • Si la ligne n'existe pas, quittez.
  • Modifiez la ligne et marquez le bloc comme "sale".
  • En arrière-plan, le bloc sera finalement vidé sur le disque.

(J'ai omis des détails sur l'intégrité transactionnelle ("ACID"), etc.)

Même dans le pire des cas, la tâche entière (pour une seule ligne) prend moins de 10 millisecondes. Dans le meilleur des cas, cela prend moins de 1 ms et peut être effectué un peu en parallèle avec certaines autres activités.


8 commentaires

Vous n'avez pas besoin d'iodku, car je ne veux rien insérer, mais ne faites rien si le disque n'existe pas. J'ai juste peur de faire des requêtes inutiles (deux requêtes et quelques calculs pour calculer la valeur de mise à jour) dans tous les cas où l'enregistrement ciblé n'existe pas .. N'existe-t-il pas un moyen de faire les requêtes, les claculations et les mises à jour UNIQUEMENT si le dossier n'existe pas? Ou est-ce que les requêtes sont traitées et court-circuitées en premier de toute façon? Tu comprends ce que je veux dire?


@DevelJoe - Vrai. Je vous donne juste un conseil dont vous pourriez avoir besoin demain.


Et j'ai ajouté plus de discussion.


merci beaucoup, mais je n'arrive toujours pas à le faire fonctionner, il dit toujours qu'il y a une erreur de syntaxe sur la ligne UPDATE ..


Voyons l'erreur de syntaxe; il pointe vers le jeton incriminé.


Je ne comprends pas ce que vous entendez par ce commentaire? Et aussi, merci beaucoup pour votre réponse très intéressante; Je suis juste au moment de commencer à comprendre comment les requêtes / commandes sont traitées, comment / ce qui est court-circuité en SQL, etc. Je voulais vous demander si vous avez des vidéos / tutoriels de support qui expliquent bien ces choses / des outils qui aident à dépanner / analyser précisément les exécutions de requêtes, etc. Non pas que je n'en ai pas trouvé beaucoup déjà en ligne, mais ce serait formidable de savoir quels outils les développeurs expérimentés utilisent à cette fin! Aussi; est UPDATE ... FROM SELECT n / a dans mariadb ??


eversql.com/sql-syntax-check-validator dit également que leur syntaxe est valide, wth ...


Merci mec, voir ma réponse ci-dessous, comme ça ça a marché, mais wow c'était une douleur. Il semble que vous aviez raison et que les clauses WHERE effectuent effectivement une sorte de court-circuit, si la clause interrogée ne renvoie aucun enregistrement correspondant. Merci, et j'attends toujours les outils / tutoriels / vids / pages que vous recommandez d'utiliser pour les optimisations de requêtes lorsqu'il s'agit de tests de performances, etc. :)



0
votes

Il n'y a pas de IF dans SQL, car il n'est pas nécessaire:


UPDATE people p
SET calculated_value = c.val
FROM (
        SELECT ID, val
        FROM
        ... complex_queries_and_calculations
        ) c
WHERE c.ID = p.ID
AND ID = 168
AND v.val <> i.val -- maybe add this to avoid idempotent updates. Beware of NULLs, though!
        ;

~


3 commentaires

bravo, mais cela fonctionne-t-il pour mariadb? une seconde je vais vérifier ..


Je ne sais pas. La syntaxe de update ... from ... peut différer un peu pour mysql.


non, ne fonctionne pas; il dit une erreur de syntaxe sur 'FROM (SELECT ...)'; c'est tellement frustrant, il semble que mariadb manque de certaines fonctionnalités. Quel est le meilleur SGBDR que vous recommandez? Et, avez-vous une autre solution, compatible avec mariadb? (aussi, veuillez noter que mon SGBDR est mariadb, pas mysql, ils sont en effet différents en termes de fonctions disponibles ..)



0
votes

GOT IT, la requête suivante fonctionne pour faire exactement ce que je voulais dans Mariadb:

UPDATE target_table

LEFT JOIN (complex_queries_and_calculations_to_get_update_value AS update_value) t

ON target_table.ID <> t.update_value -- serves just to have update value in memory, 
-- because it needs to be accessed twice to create the updated column value 
-- on update, sort of a workaround for CTE + UPDATE in MariaDB

SET target_column = JSON_ARRAY( FORMAT_UPDATE_VALUE(t.update_value),
                    FORMAT_2_UPDATE_VALUE(t.update_value) )

WHERE ID = 128 AND t.update_value IS NOT NULL;

Si l'enregistrement n'existe pas, la requête prend environ 0,0006 secondes pour s'exécuter, sans rien faire sur la table. S'il se ferme, il faut 0,0014 secondes pour s'exécuter, tout en mettant à jour l'enregistrement ciblé en conséquence. Donc, cela semble effectivement fonctionner et les ressources sont sauvegardées si l'enregistrement ciblé n'est pas trouvé dans target_table. Un grand merci à tous ceux qui ont aidé!


0 commentaires