128

In some cases, running an UPDATE statement in production can save the day. However a borked update can be worse than the initial problem.

Short of using a test database, what are options to tell what an update statement will do before running it?

static_rtti
  • 53,760
  • 47
  • 136
  • 192

10 Answers10

116

What about Transactions? They have the ROLLBACK-Feature.

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

For example:

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

Answer on question from @rickozoe below:

In general these lines will not be executed as once. In PHP f.e. you would write something like that (perhaps a little bit cleaner, but wanted to answer quick ;-) ):

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

Another way would be to use MySQL Variables (see https://dev.mysql.com/doc/refman/5.7/en/user-variables.html and 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;

But I would suggest to use the language wrappers available in your favorite programming language.

Marcel Lange
  • 1,504
  • 1
  • 13
  • 15
  • 1
    This will have unexpected results with nested transactions. – scones Nov 08 '16 at 11:48
  • 1
    Can you please give an example? – Marcel Lange Dec 05 '16 at 11:10
  • @JCM and others, how can you know if it success the update statement succeed on line 3 so you can commit and rollback ? – ricko zoe Apr 09 '17 at 23:12
  • I ran this `START TRANSACTION;` and then my query in MySQL Workbench, and it committed without any additional `COMMIT;` , luckily my query worked as expected :) So, I'm here to say: be careful and read other suggestions before trying this one! – RAM237 Apr 07 '22 at 11:06
  • Note COMMIT/ROLLBACK is not supported for tables using a MyISAM engine. Change them to a InnoDB engine, and turn off autocommit to get these to work. – will Apr 16 '22 at 03:16
71

In addition to using a transaction as Imad has said (which should be mandatory anyway) you can also do a sanity check which rows are affected by running a select using the same WHERE clause as the UPDATE.

So if you UPDATE is

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

The following will show you which rows will be updated:

SELECT *
FROM foo
WHERE col1 = 1
  AND col2 = 'foobar';
  • 1
    Using transactions is better in order to check data then. Assuming he wants to check the result, I conclude his statement is more complex than a 'SET bar = 42', so within his session he will be able to make several queries to test the resulting set of data ... – Imad Moqaddem Jun 13 '12 at 09:06
  • 6
    @ImadMoqaddem: I agree and that's why I wrote "*Apart from using a transaction as Imad said*" –  Jun 13 '12 at 09:15
  • Also work with DELETE. – R0m1 Jan 27 '22 at 08:58
  • lol, but if it too complicated? it does not help ! If records updates frequently - its also did not help ! – Vladimir Ch Jan 25 '23 at 11:23
66

Set Autocommit to OFF.

In MySQL, set autocommit=0; sets the autocommit off for the current session.

You execute your statement, see what it has changed, and then rollback if it's wrong or commit if it's what you expected!

The benefit of using transactions instead of running select query is that you can check the resulting set easily.

TylerH
  • 20,799
  • 66
  • 75
  • 101
Imad Moqaddem
  • 1,453
  • 9
  • 11
  • 12
    Just remember to commit or rollback the transaction quickly, or you risk blocking other transactions - and in the worst case bringing your application to a grinding halt. Not a good idea to execute the query, then have lunch, then come back to see the results! :-) – Gary McGill Jun 13 '12 at 09:04
  • @GaryMcGill: the pending transaction would (at least in modern DBMS) only block other *write* transactions though. –  Jun 13 '12 at 09:16
  • Unfortunately I don't think the antiquated version of MySQL I have to use supports transactions... – static_rtti Jun 13 '12 at 09:25
  • MySQL let you specify the engine to use for each table. And even very old versions of MySQL can be enriched with the InnoDB engine (which supports transactions). But transactions have a cost, it depends on the need. – Denys Séguret Jun 13 '12 at 09:26
  • 5
    @dystroy : Unfortunately, MyISAM is used everywhere, and I'm not the DBA. – static_rtti Jun 13 '12 at 09:31
  • This will have unexpected results with nested transactions. – scones Nov 08 '16 at 11:47
13

For testing update, hash # is your friend.

If you have an update statement like:

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

You hash UPDATE and SET out for testing, then hash them back in:

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

It works for simple statements.

An additional practically mandatory solution is, to get a copy (backup duplicate), whenever using update on a production table. Phpmyadmin > operations > copy: table_yearmonthday. It just takes a few seconds for tables <=100M.

TylerH
  • 20,799
  • 66
  • 75
  • 101
Johan
  • 637
  • 7
  • 11
7

I've seen many borked prod data situations that could have been avoided by typing the WHERE clause first! Sometimes a WHERE 1 = 0 can help with putting a working statement together safely too. And looking at an estimated execution plan, which will estimate rows affected, can be useful. Beyond that, in a transaction that you roll back as others have said.

You can also use WHERE FALSE for MySQL, but keep in mind other DBMSes like SQL Server won't accept that.

TylerH
  • 20,799
  • 66
  • 75
  • 101
David M
  • 71,481
  • 13
  • 158
  • 186
6

One more option is to ask MySQL for the query plan. This tells you two things:

  • Whether there are any syntax errors in the query, if so the query plan command itself will fail
  • How MySQL is planning to execute the query, e.g. what indexes it will use

In MySQL and most SQL databases the query plan command is describe, so you would do:

describe update ...;
Yawar
  • 11,272
  • 4
  • 48
  • 80
4

Just run an EXPLAIN query. So just write the word EXPLAIN before your query and it will give you info about how it would execute your update - finding rows, etc. But it won't execute it. However it will let you know if there are any syntax errors. So just use an explain!

EXPLAIN UPDATE ... SET ...
user3413723
  • 11,147
  • 6
  • 55
  • 64
3

make a SELECT of it,

like if you got

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

convert it to

SELECT * FROM users WHERE name='jan'

EaterOfCode
  • 2,202
  • 3
  • 20
  • 33
3

In these cases that you want to test, it's a good idea to focus on only current column values and soon-to-be-updated column values.

Please take a look at the following code that I've written to update WHMCS prices:

# 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

This way we clearly compare already existing values versus new values.

Mohammad Naji
  • 5,372
  • 10
  • 54
  • 79
0

Run select query on same table with all where conditions you are applying in update query.

manurajhada
  • 5,284
  • 3
  • 24
  • 43