1

我有两个表 tableA 和 tableB。tableA 有 200 万条记录,tableB 有超过 1000 万条记录。tableA 有三十多列,而 tableB 只有两列。我需要通过连接两个表来从 tableB 更新 tableA 中的列。

UPDATE tableA a 
INNER JOIN tableB b  ON a.colA=b.colA
 SET a.colB= b.colB 

两个表中的 colA 都已编入索引。

现在,当我执行查询需要几个小时。老实说,我从未见过它完成,我等了最多 5 个小时。他们有什么方法可以在 20-30 分钟内完成此查询。我应该采取什么方法。

解释 SQL 查询

"id" "_type" "table" "type" "possible_" "key" "key_len"   "ref"   "rows" "Extra"
"1" "SIMPLE" "a"     "ALL"  "INDX_DESC" \N    \N          \N   "2392270"  "Using where"
"1" "SIMPLE" "b"     "ref"  "indx_desc" "indx_desc" "133" "cis.a.desc" "1"  "Using where"
4

4 回答 4

2

您的UPDATE操作正在对一个大表的一千万行执行单个事务。UPDATE(如果由于任何原因没有完成,DBMS 会保存足够的数据来回滚整个查询。)这种大小的事务对于您的服务器来说处理起来很慢。

当您处理整个表时,该操作不能像具有高度选择性WHERE子句时那样使用索引。

有几件事要尝试:

1)除非他们需要,否则不要更新行。跳过已经具有正确值的行。如果大多数行已经具有正确的值,这将使您的更新更快。

    UPDATE tableA a 
INNER JOIN tableB b  ON a.colA=b.colA
       SET a.colB = b.colB
     WHERE a.colB <> b.colB 

2)以几千行为单位进行更新,并重复更新操作,直到整个表被更新。我猜 tableA 包含一个 id 列。您可以使用它来组织要更新的行块。

    UPDATE tableA a 
INNER JOIN tableB b  ON a.colA=b.colA
       SET a.colB = b.colB
     WHERE a.id IN  (
             SELECT a.id
               FROM tableA
              INNER JOIN tableB ON a.colA = b.colA
              WHERE a.colB <> b.colB
              LIMIT 5000
      ) 

子查询查找尚未更新的 5000 行的 id 值,然后 UPDATE 查询对其进行更新。重复这个查询,直到它没有改变任何行,你就完成了。这使事情变得更快,因为服务器必须只处理较小的事务。

3)根本不进行更新。相反,每当您需要检索 colB 值时,只需在选择查询中加入 tableB 即可。

于 2018-08-19T12:50:11.653 回答
0

分块是正确的方法。然而,块上PRIMARY KEYtableA

我建议一次只写 1000 行。

按照这里给出的提示

你说tableA的PK是varchar吗?没问题。请参阅该链接中的第二种代码;它用于ORDER BY id LIMIT 1000,1查找下一个块的结尾,而不管id(PK)的数据类型如何。

于 2018-08-27T05:08:02.583 回答
-1

为了更新单个 MySQL 表的大约 7000 万条记录,我编写了一个存储过程来更新表,以 5000 个块为单位。大约需要 3 个小时才能完成。

DELIMITER $$
DROP PROCEDURE IF EXISTS update_multiple_example_proc$$
CREATE PROCEDURE update_multiple_example_proc()
BEGIN
DECLARE x  bigint;

SET x = 1;

WHILE x  <= <MAX_PRIMARY_KEY_TO_REACH> DO
UPDATE tableA A
   JOIN tableB B
   ON A.col1 = B.col1
SET A.col2_to_be_updated = B.col2_to_be_updated where A.id between x and x+5000 ;
SET  x = x + 5000;
END WHILE;

END$$
DELIMITER ;
于 2019-05-20T11:34:25.497 回答
-1

嗨,我不确定,但你可以通过 cron 工作来完成。流程:在表tableA中需要再添加一个字段(例如)is_update 设置它的默认值为0,每分钟设置一次cron作业。当 cron 工作时:例如,它选择第一次 10000 记录具有 is_update 字段 0 值并更新记录并设置 is_update is1,第二次选择下一个 10000 具有 is_update 0 等等......希望这对你有帮助。

于 2018-08-19T14:15:10.797 回答