burnersk
burnersk

Reputation: 3490

How to convert a MySQL select statement with a subquery to the same table into an update statement?

I have the following working select statement.

SELECT t1.id, t1.option_key, ( 
    SELECT
        t3.content AS option_value
    FROM tblfoo t2
    LEFT OUTER JOIN tblbar t3 ON( t3.refid = t2.id )
    WHERE t2.id = t1.id
    LIMIT 1
) AS option_value
FROM tblfoo t1

The table structure looks like the following:

| tblfoo                         |
+----+------------+--------------+
| id | option_key | option_value |
+----+------------+--------------+
|  1 | foo        | NULL         |
|  2 | bar        | NULL         |
|  3 | baz        | NULL         |


| tblbar               |
+----+-----------------+
| id | refid | content |
+----+-----------------+
|  1 |     1 | value1  |
|  1 |     2 | value2  |
|  1 |     3 | value3  |

The result of the update statement should be this:

| tblfoo                         |
+----+------------+--------------+
| id | option_key | option_value |
+----+------------+--------------+
|  1 | foo        | value1       |
|  2 | bar        | value2       |
|  3 | baz        | value3       |

I want to update option_value with tblfoo with the related data from content from tblbar. Unfortunately, tblbar may have multiple entries for the refid with the same value. This is why there is a need for LIMIT 1 (or GROUP BY t,id or DISTINCT) with the subquery.

I have figured out, that the query is significant faster, when I do subquery with LIMIT 1 instead of subquery with SELECT DISTINCT or a join in combination with GROUP BY t1.id. So after execution time optimization, I ended up with the select statement above.

There is also a catch with the source table, which should be updated. option_value is a actual field, that also exists in the source table (but with a NULL value).

The issues I ran into while trying to convert the optimized select statement above into an update statement was mainly that I cannot access t1.id from inside the subquery.

How to convert the select statement into an update statement without losing the performance optimization?

Upvotes: 0

Views: 759

Answers (3)

Gordon Linoff
Gordon Linoff

Reputation: 1270713

If you are updating all rows, then the most efficient method might be:

UPDATE tblfoo f
    SET f.option_value = (SELECT b.content FROM tblbar WHERE f.id = b.refid LIMIT 1);

In particular, this can take advantage of an index on tblbar(refid, content).

Upvotes: 0

Tim Biegeleisen
Tim Biegeleisen

Reputation: 522471

Just use an update join with a subquery which finds distinct values in tblbar:

UPDATE tblfoo f
INNER JOIN
(
    SELECT DISTINCT refid, content
    FROM tblbar
) b
    ON f.id = b.refid
SET f.option_value = b.content;

Upvotes: 2

Madhur Bhaiya
Madhur Bhaiya

Reputation: 28854

Your correlated subquery can further be optimized by avoid the Left Join between the two tables. Instead, you can directly get the content value from the second table.

For updating, you can use your select query as a Derived table, and join it to the source table:

UPDATE tblfoo AS tfoo 
JOIN (
      SELECT t1.id, 
             (SELECT t3.content AS option_value
              FROM tblbar t3
              WHERE t3.refid = t1.id
              LIMIT 1
             ) AS option_value
      FROM tblfoo t1
     ) AS dt ON dt.id = tfoo.id 
SET tfoo.option_value = dt.option_value;

Upvotes: 1

Related Questions