【问题标题】:How to update multiple columns based on values from an associated table?如何根据关联表中的值更新多个列?
【发布时间】:2013-08-16 08:05:56
【问题描述】:

有两个名为 mastersversions 的表。 versions 表包含 master 表在不同时间点的条目。

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

保证每个masters条目都有至少一个 versions条目。

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

这些表格也可以在 SQL Fiddle 中找到。
可以为 masters 记录2 选择每个 master 记录的最新 version,如下例所示:

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

如何使用 one 中的 latest version 更新 masters 表的 每条记录命令?我想覆盖 nameadded_at 列的值。请注意,versions 表中的其他列在 masters 表中不存在,例如 notes

是否可以使用 JOIN 完成更新,以便在较大的表上执行得更快?

【问题讨论】:

    标签: mysql sql sql-update sqldatetime sql-limit


    【解决方案1】:

    不需要两次触发子查询。

    以下是更新说明

    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;
    

    【讨论】:

    • 我删除了与特定..id = 2 匹配的部分。运行您的示例然后由于某种原因未能使用id = 4, d-old 更新 masters 行。
    • 现在检查this link。如果正确,我会修改帖子
    • 看起来不错。同时,当我将它们介绍给我的问题时,它似乎也适用于其他列。请更新您的答案。顺便说一句:“菲德尔”中的错字。
    • @JJD 我已经修改了我的帖子。看看吧。
    • 您的解决方案的优点是 masters 表可以包含 附加列。 (+1) 虽然明确定义每一对,例如m.name = v.name,但在将来更改列时有点不灵活。
    【解决方案2】:

    这可能对你有用,试试这个:

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

    【讨论】:

    • 我希望避免为每个单独的列运行单独的 SELECT 语句。
    【解决方案3】:
    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;
    

    【讨论】:

    • 我想避免为每个单独的列运行单独的 SELECT 语句。
    【解决方案4】:

    这可能会满足您的需求:

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

    请注意,这依赖于主键在 id 上具有主键并且是特定于 MySQL 的。

    【讨论】:

    • 这会在 masters 表中生成重复项。
    • @JJD masters 表有主键吗?我注意到,如果没有一个,这将无法工作。
    • 谢谢!我在 SQL Fiddle 中添加了 PRIMARY KEY
    • 当我想更新特定列时这是否也有效?我修改了我的问题以包含这个事实。
    • @JDD。是的,选择只会从您需要的版本中选择列,因此版本可以有更多列。
    【解决方案5】:

    目前无法在 mysql 上进行测试,但这应该可以在 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))
    

    【讨论】:

    • 我想避免为每个单独的列运行单独的 SELECT 语句。
    猜你喜欢
    • 1970-01-01
    • 2016-11-11
    • 2021-10-28
    • 1970-01-01
    • 2012-04-17
    • 2021-03-24
    • 2022-11-16
    • 1970-01-01
    • 2019-08-20
    相关资源
    最近更新 更多