JJD
JJD

Reputation: 51864

How to update multiple columns based on values from an associated table?

There are two tables named masters and versions. The versions table holds entries of the master table at different points in time.

-------------------------
 masters
-------------------------
 id |  name |  added_at
----+-------+------------
  1 | a-old | 2013-08-13
  2 | b-new | 2012-04-19
  3 | c-old | 2012-02-01
  4 | d-old | 2012-12-24

It is guaranteed that there is at least one versions entry for each masters entry.

---------------------------------------------   
 versions
---------------------------------------------
 id |  name |   added_at | notes | master_id
----+-------+--------------------------------
  1 | a-new | 2013-08-14 | lorem |         1
  1 | a-old | 2013-08-13 | lorem |         1
  2 | b-new | 2012-04-19 | lorem |         2
  3 | c-old | 2012-02-01 | lorem |         3
  4 | d-new | 2013-02-20 | lorem |         4
  5 | d-old | 2012-12-24 | lorem |         4

The tables can also be found in this SQL Fiddle.
The latest version of each master record can be selected as shown in this example for masters record 2:

SELECT * FROM versions
WHERE master_id = 2
ORDER BY added_at DESC
LIMIT 1;

How can I update each record of the masters table with its latest version in one command? I want to overwrite the values for both the name and added_at columns. Please note, there are additional columns in the versions table which do not exist in the masters table such as notes.

Can the update been done with a JOIN so it performs fast on larger tables?

Upvotes: 2

Views: 429

Answers (5)

Romesh
Romesh

Reputation: 2274

There is no need to fire subquery twice.

Below is the update statement

update masters m, (
  select id, name, added_at, master_id 
  from versions 
  order by added_at desc
) V
set
  m.name = v.name, 
  m.added_at = v.added_at     
where v.master_id = m.id;

Upvotes: 1

Jivan
Jivan

Reputation: 1320

This might work for you, try this:

UPDATE masters m 
SET m.name = (SELECT v.name FROM versions v WHERE
m.id = v.master_id ORDER BY v.added_at DESC LIMIT 1), 
m.added_at =
(SELECT v.added_at FROM versions v WHERE m.id = v.master_id ORDER BY
v.added_at DESC LIMIT 1);

Upvotes: 0

Bere
Bere

Reputation: 1747

update masters set 
name = (SELECT name FROM versions
       WHERE master_id = masters.id
       ORDER BY added_at DESC
       LIMIT 1),
added_at = (SELECT added_at FROM versions
        WHERE master_id = masters.id
        ORDER BY added_at DESC
        LIMIT 1)
where id = 2;

Upvotes: 0

Jim
Jim

Reputation: 22656

This might do what you need:

REPLACE INTO masters
    SELECT v.master_id,v.name,v.added_at 
    FROM versions v 
    WHERE v.added_at = (SELECT MAX(vi.added_at) 
                       FROM versions vi 
                       WHERE  vi.master_id = v.master_id);

Note that this relies on masters having a primary key on id and is MySQL specific.

Upvotes: 1

Bruellhusten
Bruellhusten

Reputation: 318

Can't test it on mysql right now, but this should work on MSSQL

UPDATE masters AS m
SET 
  m.name = 
     (SELECT v.Name FROM versions AS v WHERE v.id = m.id AND v.added_at = 
     (SELECT MAX(v2.added_at) FROM versions As v2 WHERE v2.id = v.id))
  m.added_at = 
     (SELECT v.added_at FROM versions AS v3 WHERE v3.id = m.id AND v3.added_at = 
     (SELECT MAX(v4.added_at) FROM versions As v4 WHERE v4.id = v3.id))

Upvotes: 0

Related Questions