2

Heyo,

I'm confused as to how this works:

UPDATE original 
SET price=(SELECT price FROM new WHERE new.id=original.id) 
WHERE id IN (SELECT id FROM new);

What it does is updating an original table with items with the prices of another table. The second table (my update table) has two columns, ID (which matches the corresponding ID of the first table) and a column for the new price.

As far as I understand, MySQL collects all columns that have a corresponding entry in the second table in the third row. And then it somehow magically updates the price column with a price column of the joint values created from the tables new and original. How does it determine which value goes where? How does it make sure nothing goes wrong if there is an ID used twice in the table with the new values?

Some clarification would be nice. Thanks in advance, Yorrd

EDIT:

My question is, how does MySQL determine which row to match with which new value from the second table. Even if they are unique, how is being made sure that they're not mixed up accidentally?

  • It doesn't make sure (nothing goes wrong.) If new(id) is primary or unique, it's fine. Otherwise, don't use this. Or, you can try it, it will throw an error like ("subquery returns more than one value") – ypercubeᵀᴹ Aug 5 '14 at 21:52
  • @ypercube so how would I make sure? Maybe my question should be: How do I make sure an UPDATE statement uses a joint table correctly? – Yorrd Aug 5 '14 at 21:54
  • You have a SET price= (SELECT ....) Make sure that the subquery returns 1 value always. In this case, the new (id) should have a unique or primary key constraint. Does it have? – ypercubeᵀᴹ Aug 5 '14 at 21:56
  • @ypercube it does. ID is a primary key. How does it get matched to the right corresponding line in the old table though? Why is it not mixed up sometimes? How does MySQL determine which value to put in which row in the original table? – Yorrd Aug 5 '14 at 22:07
  • @DBZ_A yeah :D but the last step you described ("find the price for those ids from "new" and use that value to update "original"") is what I'm wondering about. It finds the prices and updates the column in the original table. But how does it know which value to put in which row in the original table? For example I got A, B and C in the first table with the IDs 1, 2 and 3 respectively. In the second table I got two rows: 1, 1$ and 3, 12$. How does it make sure the 12$ from the third row is used to update the original item with ID 3 and not the one with ID 1? – Yorrd Aug 5 '14 at 23:34
3

The answer is in the parentheses:

SET price=(SELECT price FROM new WHERE new.id=original.id) 

This is a scalar subquery. It only ever returns one value (or no value, in which case it effectively returns NULL). As @ypercube mentioned in comments, if there were ever more than one matching id in new then this expression would throw an error, since a scalar subquery can't and won't deal with more than one possible value.

This is also (by at least some definitions) a correlated subquery, containing a reference to a table (original) that is not mentioned in the FROM clause of the subquery.

The subquery is, essentially, executed once for each row in original in order to find the needed value in new, and that's how the rows don't get mixed up -- this expression is evaluated for each row in the original table.

At least, conceptually, that's how it happens. The optimizer is free to decide -- within the limits of its design -- if there's a better way to perform your query than the way you've written it, as long as the chosen approach will still provide the exact same result (with the exception of the ordering of rows, which is by definition, undefined, unless you explicitly ORDER BY in a SELECT statement). MySQL 5.6 brought some changes in subquery optimization that were largely improvements.

Although sometimes they are absolutely essential, subqueries can be a red flag that a query's logic could be improved, to make things easier on the optimizer and get the work done faster. This is one of those cases.

A better, arguably clearer, and perhaps significantly better-performing way to write the example query would be this:

UPDATE original o
  JOIN new n ON n.id = o.id
   SET o.price = n.price;

There's no need to include WHERE n.id = o.id because the join will not only join the rows on that criterion, it will also exclude all rows that can't be joined. The caveat here is that if id in the new table isn't unique, for a different reason than the error the original query would throw. In this case, the result isn't deterministic, because you're not able to tell the server which of the more-than-one potentially matching row, so it will pick one, and you can't choose which one. But if id is unique in the new table, there's no problem.

MySQL has historically been not the best at handling WHERE ... IN ( ... ) in some cases, and this rewrite also removes the need for that construct.

  • perfect. Exactly what I was looking for. Thanks a lot :) – Yorrd Aug 5 '14 at 23:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.