简体   繁体   中英

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?

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;

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;

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) .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM