Comment tester une instruction SQL Update avant de l'exécuter?


96

Dans certains cas, l'exécution d'une instruction UPDATE en production peut sauver la situation. Cependant, une mise à jour bloquée peut être pire que le problème initial.

À moins d'utiliser une base de données de test, quelles sont les options pour dire ce qu'une instruction de mise à jour fera avant de l'exécuter?

Réponses:


50

En plus d'utiliser une transaction comme Imad l'a dit (ce qui devrait être obligatoire de toute façon), vous pouvez également faire un contrôle de cohérence sur les lignes affectées en exécutant une sélection en utilisant la même clause WHERE que UPDATE.

Donc, si vous UPDATE est

UPDATE foo
  SET bar = 42
WHERE col1 = 1
  AND col2 = 'foobar';

Ce qui suit vous montrera quelles lignes seront mises à jour:

SELECT *
FROM foo
WHERE col1 = 1
  AND col2 = 'foobar';

1
Il est préférable d'utiliser des transactions pour vérifier les données. En supposant qu'il veuille vérifier le résultat, je conclus que sa déclaration est plus complexe qu'un 'SET bar = 42', donc au cours de sa session, il sera en mesure de faire plusieurs requêtes pour tester l'ensemble de données résultant ...
Imad Moqaddem

3
@ImadMoqaddem: Je suis d'accord et c'est pourquoi j'ai écrit " En plus d'utiliser une transaction comme l'a dit Imad "
a_horse_with_no_name

Et si FOREIGN KEY UPDATE CASCADEvotre sql échoue
Vert

@Green: que voulez-vous dire par "échouer"?
a_horse_with_no_name

73

Qu'en est-il des transactions? Ils ont la fonction ROLLBACK.

@voir https://dev.mysql.com/doc/refman/5.0/en/commit.html

Par exemple:

START TRANSACTION;
SELECT * FROM nicetable WHERE somthing=1;
UPDATE nicetable SET nicefield='VALUE' WHERE somthing=1;
SELECT * FROM nicetable WHERE somthing=1; #check

COMMIT;
# or if you want to reset changes 
ROLLBACK;

SELECT * FROM nicetable WHERE somthing=1; #should be the old value

Répondez à la question de @rickozoe ci-dessous:

En général, ces lignes ne seront pas exécutées une seule fois. En PHP, vous écririez quelque chose comme ça (peut-être un peu plus propre, mais je voulais répondre rapidement ;-)):

$MysqlConnection->query('START TRANSACTION;');
$erg = $MysqlConnection->query('UPDATE MyGuests SET lastname='Doe' WHERE id=2;');
if($erg)
    $MysqlConnection->query('COMMIT;');
else
    $MysqlConnection->query('ROLLBACK;');

Une autre façon serait d'utiliser les variables MySQL (voir https://dev.mysql.com/doc/refman/5.7/en/user-variables.htm l et https://stackoverflow.com/a/18499823/1416909 ):

# do some stuff that should be conditionally rollbacked later on

SET @v1 := UPDATE MyGuests SET lastname='Doe' WHERE id=2;
IF(v1 < 1) THEN
    ROLLBACK;
ELSE
    COMMIT;
END IF;

Mais je suggérerais d'utiliser les wrappers de langage disponibles dans votre langage de programmation préféré.


1
Cela aura des résultats inattendus avec les transactions imbriquées.
scones

Pouvez-vous donner un exemple?
Marcel Lange

@JCM et autres, comment pouvez-vous savoir si la déclaration de mise à jour réussit à la ligne 3 afin que vous puissiez valider et annuler?
ricko zoe

56

Autocommit OFF ...

MySQL

set autocommit=0;

Il désactive la validation automatique pour la session en cours.

Vous exécutez votre instruction, voyez ce qu'elle a changé, puis annulez si elle est incorrecte ou validez si c'est ce que vous attendiez!

EDIT: L'avantage d'utiliser des transactions au lieu d'exécuter une requête de sélection est que vous pouvez vérifier plus facilement l'ensemble résultant.


4
@dystroy: chaque SGBD sensible prend en charge les transactions.
a_horse_with_no_name

7
N'oubliez pas de valider ou d'annuler la transaction rapidement, sinon vous risquez de bloquer d'autres transactions - et dans le pire des cas, de mettre votre application à l'arrêt. Ce n'est pas une bonne idée d'exécuter la requête, puis de déjeuner, puis de revenir voir les résultats! :-)
Gary McGill

@GaryMcGill: la transaction en attente ne bloquerait (au moins dans le SGBD moderne) que les autres transactions d' écriture .
a_horse_with_no_name

5
@dystroy: Malheureusement, MyISAM est utilisé partout, et je ne suis pas le DBA.
static_rtti

1
Déclaration SQL ajoutée :)
Imad Moqaddem

11

Je sais que c'est une répétition d'autres réponses, mais cela a un certain soutien émotionnel pour faire le pas supplémentaire pour tester la mise à jour: D

Pour tester la mise à jour, hash # est votre ami.

Si vous avez une déclaration de mise à jour comme:

UPDATE 
wp_history
SET history_by="admin"
WHERE
history_ip LIKE '123%'

Vous hachez UPDATE et SET pour les tester, puis les hachez à nouveau:

SELECT * FROM
#UPDATE
wp_history
#SET history_by="admin"
WHERE
history_ip LIKE '123%'

Cela fonctionne pour les déclarations simples.

Une autre solution pratiquement obligatoire consiste à obtenir une copie (copie de sauvegarde) chaque fois que vous utilisez la mise à jour sur une table de production. Phpmyadmin> opérations> copie: table_yearmonthday. Cela ne prend que quelques secondes pour les tables <= 100M.


5

Ce n'est pas une réponse directe, mais j'ai vu de nombreuses situations de données de prod embarrassées qui auraient pu être évitées en tapant d'abord la WHEREclause ! Parfois, un WHERE 1 = 0peut aider à rédiger une déclaration de travail en toute sécurité. Et regarder un plan d'exécution estimé, qui évaluera les lignes affectées, peut être utile. Au-delà de cela, dans une transaction que vous annulez comme d'autres l'ont dit.


2
@SystemParadox - rien, cependant, WHERE 1 = 0n'est plus portable si quelqu'un rencontre cela qui travaille avec un SGBD différent. Par exemple, SQL Server n'acceptera pas WHERE FALSE.
David M

2

Dans les cas que vous souhaitez tester, il est judicieux de se concentrer uniquement sur les valeurs de colonne actuelles et sur les valeurs de colonne bientôt mises à jour .

Veuillez jeter un œil au code suivant que j'ai écrit pour mettre à jour les prix WHMCS:

# UPDATE tblinvoiceitems AS ii

SELECT                        ###  JUST
    ii.amount AS old_value,   ###  FOR
    h.amount AS new_value     ###  TESTING
FROM tblinvoiceitems AS ii    ###  PURPOSES.

JOIN tblhosting AS h ON ii.relid = h.id
JOIN tblinvoices AS i ON ii.invoiceid = i.id

WHERE ii.amount <> h.amount   ### Show only updatable rows

# SET ii.amount = h.amount

De cette façon, nous comparons clairement les valeurs déjà existantes par rapport aux nouvelles valeurs.


1

Exécutez la requête de sélection sur la même table avec toutes les whereconditions que vous appliquez dans la requête de mise à jour.


0

en faire un SELECT,

comme si tu avais

UPDATE users SET id=0 WHERE name='jan'

le convertir en

SELECT * FROM users WHERE name='jan'

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.