Reputation: 3490
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
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
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
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