使用来自 SELECT 子查询的值的 UPDATE 查询,高效

Posted

技术标签:

【中文标题】使用来自 SELECT 子查询的值的 UPDATE 查询,高效【英文标题】:UPDATE query with values from SELECT subquery, efficiently 【发布时间】:2015-03-26 00:54:57 【问题描述】:

我试图提出一个查询,该查询使用同一个表中的其他记录更新 mysql 表中的记录,但我在本地测试和生产之间得到了混合结果。我对子查询了解不多,所以我想把这个问题带到这里。在使用 MySQL InnoDB 5.6.23 进行本地开发时,对大约 18 万条记录的数据集的查询需要 25 到 30 秒。在具有 MySQL InnoDB 5.5.32 和 254k 记录数据集的暂存服务器上,查询似乎会停止数小时,直到它停止,占用了 100% 的 CPU 内核。

这是我想出的查询:

UPDATE
    `product_lang` AS `pl1`
SET
    pl1.`name` = (
        SELECT pl2.`name` FROM (SELECT `name`, `id_product`, `id_lang` FROM `product_lang`) AS `pl2`
        WHERE pl1.`id_product` = pl2.`id_product`
        AND pl2.`id_lang` = 1
    )
WHERE
    pl1.`id_lang` != 1

目标是将id_lang 不为1 的产品记录中name 的值(为了解释的默认语言)替换为记录值的name 的值与默认的id_lang 的值1.

我知道子查询效率低下,但我真的不知道如何解决这个问题,将其留在 SQL 领域而不是使用应用程序层来完成繁重的工作将是一个很好的加分。

【问题讨论】:

【参考方案1】:

如果你这样写查询:

UPDATE product_lang pl1
    SET pl1.name = (SELECT pl2.`name`
                    FROM (SELECT `name`, `id_product`, `id_lang`
                          FROM `product_lang`
                         ) `pl2`
                    WHERE pl1.`id_product` = pl2.`id_product` AND pl2.`id_lang` = 1
                   )
WHERE pl1.`id_lang` <> 1

那你就有问题了。唯一可以提供帮助的索引是product_lang(id_lang)

我建议将其写成join

UPDATE product_lang pl1 join
       (select id_product, pl.name
        from product_lang
        where id_lang = 1
       ) pl2
       on pl1.id_lang <> 1 and pl2.id_product = pl1.id_product
    SET pl1.name =  pl2.name
WHERE pl1.id_lang <> 1

此查询所需的索引是product_lang(id_lang, id_product)product_lang(id_product)。但是,这似乎是一个奇怪的update,因为它会将所有名称设置为语言 1 中的名称。

【讨论】:

查询需要将每个产品的所有语言重置为默认语言。这就是为什么它很奇怪! ;)【参考方案2】:
UPDATE product_lang AS pl1
  JOIN product_lang AS pl2
      ON pl1.`id_product` =
         pl2.`id_product`
  SET pl1.name = pl2.name
  WHERE pl2.`id_lang`  = 1
    AND pl1.`id_lang` != 1;

还有INDEX(id_lang, id_product)

【讨论】:

【参考方案3】:

确保存在指定列 id_product 和 id_lang 的索引。

【讨论】:

【参考方案4】:

更新 pl1 设置 pl1.name=pl2.name 来自 product_lang pl1 ,product_lang pl2 其中 pl1.id_product = pl2.id_product AND pl2.id_lang = 1 和pl1.id_lang 1

所需的复合索引将是 id_product 和 id_lang for

【讨论】:

以上是关于使用来自 SELECT 子查询的值的 UPDATE 查询,高效的主要内容,如果未能解决你的问题,请参考以下文章

SELECT FOR UPDATE 与子查询导致死锁

Oracle Bulk Collect and Update from cursor or user Update with Select 子查询

SQL:使用UNION或子查询后无法使用UPDATE子句,而类似的SELECT仍然有效

子查询|视图事务

在单个外部 SELECT 查询中使用来自 SELECT 子查询的两个聚合值

使用来自 Select 的值插入查询