使用来自 SELECT 子查询的值高效地更新查询
UPDATE query with values from SELECT subquery, efficiently
我试图想出一个查询,使用同一 table 中的其他记录更新 MySQL table 中的记录,但我在本地测试和生产之间得到了混合结果.我对子查询不太了解,所以我想在这里提出这个问题。在使用 MySQL InnoDB 5.6.23 的本地开发中,对大约 180k 条记录的数据集的查询需要 25 到 30 秒。在具有 MySQL InnoDB 5.5.32 和包含 254k 条记录的数据集的临时服务器上,查询似乎停滞了几个小时,直到它停止,占用了 CPU 核心的 100%。
这是我提出的查询:
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
objective是将id_lang
不为1的产品记录中name
的值(为便于说明,默认语言)替换为[=12的值=] 的记录值,默认 id_lang
为 1.
我知道子查询效率低下,但我真的不知道如何解决这个问题,如果把它留在SQL-land而不是使用应用层来做繁重的工作。
确保有一个索引指定列 id _ product 和 id_lang。
如果你这样写查询:
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 的名称。
更新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
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)
.
我试图想出一个查询,使用同一 table 中的其他记录更新 MySQL table 中的记录,但我在本地测试和生产之间得到了混合结果.我对子查询不太了解,所以我想在这里提出这个问题。在使用 MySQL InnoDB 5.6.23 的本地开发中,对大约 180k 条记录的数据集的查询需要 25 到 30 秒。在具有 MySQL InnoDB 5.5.32 和包含 254k 条记录的数据集的临时服务器上,查询似乎停滞了几个小时,直到它停止,占用了 CPU 核心的 100%。
这是我提出的查询:
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
objective是将id_lang
不为1的产品记录中name
的值(为便于说明,默认语言)替换为[=12的值=] 的记录值,默认 id_lang
为 1.
我知道子查询效率低下,但我真的不知道如何解决这个问题,如果把它留在SQL-land而不是使用应用层来做繁重的工作。
确保有一个索引指定列 id _ product 和 id_lang。
如果你这样写查询:
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 的名称。
更新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
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)
.