Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Will SQL update affect its subquery during the update run?

I'm just composing a complex update query which looks more or less like this:

update table join
    (select y, min(x) as MinX 
     from table
     group by y) as t1
    using (y)
set x = x - MinX

Which means that the variable x is updated based on the subquery, which also processes variable x - but couldn't this x already be modified by the running update command? Isn't this a problem? I mean, in normal programming you normally have to handle this explicitly, i.e. store new value to some other place from the old value and after the job is done, replace the old value with new... but how will SQL database do this?

I'm not interested in a single observation or experiment. I would like to have a snippet from the docs or sql standard that will say what is the defined behaviour in this case. I'm using MySQL, but answers valid also for other PostgresQL, Oracle, etc. and especially for SQL standard in general are appreciated. Thanks!

like image 939
Tomas Avatar asked Apr 10 '12 15:04

Tomas


People also ask

Can UPDATE be used in a subquery?

Like SELECT , the UPDATE statement can have a subquery in several places or clauses. In an UPDATE , the two clauses in which subqueries are used most commonly are SET and WHERE . The SET clause is where we define the new value for the column being modified by the UPDATE .

Does UPDATE in SQL overwrite?

If you tell SQL Server to UPDATE some rows - it will update those rows. SQL Server doesn't do any "matching" of its own. That's up to you - you control the WHERE clause for the UPDATE . If you want to avoid updating some rows - make sure the WHERE clause excludes them.

Can we use subquery in UPDATE statement true or false?

You can use subqueries in SELECT, INSERT, UPDATE, and DELETE statements wherever expressions are allowed. For instance, you can use a subquery as one of the column expressions in a SELECT list or as a table expression in the FROM clause. A DML statement that includes a subquery is referred to as the outer query.

What are the limitations of subquery?

Subqueries cannot manipulate their results internally, that is, a subquery cannot include the order by clause, the compute clause, or the into keyword. Correlated (repeating) subqueries are not allowed in the select clause of an updatable cursor defined by declare cursor. There is a limit of 50 nesting levels.


3 Answers

** Edited **

Selecting from the target table

From 13.2.9.8. Subqueries in the FROM Clause:

Subqueries in the FROM clause can return a scalar, column, row, or table. Subqueries in the FROM clause cannot be correlated subqueries, unless used within the ON clause of a JOIN operation.

So, yes, you can perform the above query.

The problem

There are really two problems here. There's concurrency, or ensuring that no one else changes the data out from under our feet. This is handled with locking. Dealing with the actual modification of new versus old values is handled with derived tables.

Locking

In the case of your query above, with InnoDB, MySQL performs the SELECT first, and acquires a read (shared) lock on each row in the table individually. If you had a WHERE clause in the SELECT statement, then only the records you select would be locked, where ranges would cause any gaps to be locked as well.

A read lock prevents any other query from acquiring write locks, so records can't be updated from elsewhere while they're read locked.

Then, MySQL acquires a write (exclusive) lock on each of the records in the table individually. If you had a WHERE clause in your UPDATE statement, then only the specific records would be write locked, and again, if the WHERE clause selected a range, then you would have a range locked.

Any record that had a read lock from the previous SELECT would automatically be escalated to a write lock.

A write lock prevents other queries from obtaining either a read or write lock.

You can use Innotop to see this by running it in Lock mode, start a transaction, execute the query (but don't commit it), and you will see the locks in Innotop. Also, you can view the details without Innotop with SHOW ENGINE INNODB STATUS.

Deadlocks

Your query is vulnerable to a deadlock if two instances were run at the same time. If query A got read locks, then query B got read locks, query A would have to wait for query B's read locks to release before it could acquire the write locks. However, query B isn't going to release the read locks until after it finishes, and it won't finish unless it can acquire write locks. Query A and query B are in a stalemate, and hence, a deadlock.

Therefore, you may wish to perform an explicit table lock, both to avoid the massive amount of record locks (which uses memory and affects performance), and to avoid a deadlock.

An alternative approach is to use SELECT ... FOR UPDATE on your inner SELECT. This starts out with write locks on all of the rows instead of starting with read and escalating them.

Derived tables

For the inner SELECT, MySQL creates a derived temporary table. A derived table is an actual non-indexed copy of the data that lives in the temporary table that is automatically created by MySQL (as opposed to a temporary table that you explicitly create and can add indexes to).

Since MySQL uses a derived table, that's the temporary old value that you refer to in your question. In other words, there's no magic here. MySQL does it just like you'd do it anywhere else, with a temporary value.

You can see the derived table by doing an EXPLAIN against your UPDATE statement (supported in MySQL 5.6+).

like image 181
Marcus Adams Avatar answered Sep 29 '22 11:09

Marcus Adams


A proper RDBMS uses statement level read consistency, which ensures the statement sees (selects) the data as it was at the time the statement began. So the scenario you are afraid of, won't occur.

Regards,
Rob.

like image 42
Rob van Wijk Avatar answered Sep 29 '22 11:09

Rob van Wijk


Oracle has this in the 11.2 Documentation

A consistent result set is provided for every query, guaranteeing data consistency, with no action by the user. An implicit query, such as a query implied by a WHERE clause in an UPDATE statement, is guaranteed a consistent set of results. However, each statement in an implicit query does not see the changes made by the DML statement itself, but sees the data as it existed before changes were made.

like image 44
Adam Hawkes Avatar answered Sep 29 '22 10:09

Adam Hawkes