更新大型表上的行的最高效方法

Posted

技术标签:

【中文标题】更新大型表上的行的最高效方法【英文标题】:Most performant way to update rows on large tables 【发布时间】:2021-12-05 22:08:32 【问题描述】:

我有 2 张桌子:

请求:

idㅤㅤ|ㅤㅤaccountㅤㅤㅤㅤ|ㅤsubaccountㅤ|ㅤamountㅤ|ㅤㅤupdatedAmount    

1ㅤㅤ|ㅤㅤACCOUNT_1ㅤㅤ|ㅤㅤㅤ12ㅤㅤㅤ|ㅤㅤ10.2ㅤㅤ|ㅤㅤnull       
2ㅤㅤ|ㅤㅤACCOUNT_2ㅤㅤ|ㅤㅤㅤ45ㅤㅤㅤ|ㅤㅤ0ㅤㅤㅤ|ㅤㅤnull          
3ㅤㅤ|ㅤㅤACCOUNT_3ㅤㅤ|ㅤㅤㅤ11ㅤㅤㅤ|ㅤㅤ50.6ㅤㅤ|ㅤㅤnull   

 

每日数据:

idㅤㅤ|ㅤㅤaccountㅤㅤㅤㅤ|ㅤsubaccountㅤ|ㅤamountㅤ

1ㅤㅤ|ㅤㅤACCOUNT_1ㅤㅤ|ㅤㅤㅤ45ㅤㅤㅤ|ㅤㅤ3.12     
2ㅤㅤ|ㅤㅤACCOUNT_1ㅤㅤ|ㅤㅤㅤ12ㅤㅤㅤ|ㅤㅤ15        
3ㅤㅤ|ㅤㅤACCOUNT_2ㅤㅤ|ㅤㅤㅤ11ㅤㅤㅤ|ㅤㅤ1.09     
4ㅤㅤ|ㅤㅤACCOUNT_2ㅤㅤ|ㅤㅤㅤ70ㅤㅤㅤ|ㅤㅤ30.6     
5ㅤㅤ|ㅤㅤACCOUNT_3ㅤㅤ|ㅤㅤㅤ11ㅤㅤㅤ|ㅤㅤ50.6  

我需要更新 Request 表中的 updatedAmount 列,满足以下条件:

-仅当它在 DailyData 表中找到具有相同帐户和子帐户的行时才更新行。 - 仅当每个表中列数量的值不同时才会更新。

对于符合此条件的行,它将金额(来自 DailyData 表)保存在 updatedAmount(请求表)列中。

所以对于这个例子,我们会有这个输出,只有 id 1 更新:

idㅤㅤ|ㅤㅤaccountㅤㅤㅤㅤ|ㅤsubaccountㅤ|ㅤamountㅤ|ㅤㅤupdatedAmount    

1ㅤㅤ|ㅤㅤACCOUNT_1ㅤㅤ|ㅤㅤㅤ12ㅤㅤㅤ|ㅤㅤ10.2ㅤㅤ|ㅤㅤ15              
2ㅤㅤ|ㅤㅤACCOUNT_2ㅤㅤ|ㅤㅤㅤ45ㅤㅤㅤ|ㅤㅤ0ㅤㅤㅤ|ㅤㅤnull                    
3ㅤㅤ|ㅤㅤACCOUNT_3ㅤㅤ|ㅤㅤㅤ11ㅤㅤㅤ|ㅤㅤ50.6ㅤㅤ|ㅤㅤnull   

         

我最大的问题是性能。这是一个日常流程,Request 表将有超过 100 万行,而 DailyProcess 表大约有 300k 行,每天都在变化。 到目前为止,我创建了 2 个方法,但都需要很多时间。

1:

UPDATE Request r                                              
LEFT OUTER JOIN DailyData d                                              
ON r.account = d.account AND r.subaccount = d.subaccount                                              
SET r.updatedAmount = d.amount                                 

2:

UPDATE Request r, DailyData d                                              
SET r.updatedAmount = d.amount                                              
WHERE r.account = d.account AND r.subaccount = d.subaccount AND r.amount <> d.amount         
                 

有人能推荐更快的方法吗?任何帮助将不胜感激

【问题讨论】:

您创建了哪些索引?两个表上的帐户/子帐户的复合索引可能会有所帮助。 查看(并在此处分享)query plans used。这将表明查询是否可以从新索引(以及哪些索引)或其他调整中受益,以鼓励不同的计划。 查询计划选择有时还会显示其他工件,例如由于统计信息错误而从良好候选索引中选择的错误计划。还应更新问题以包含所有当前索引,作为所示架构的一部分。 最后,显示的两种更新方法具有不同的行为(第一次更新将为 DailyData 中缺失的项目分配 NULL 金额;它还将导致所有 Request 记录更新,即使值相同)。哪种行为是正确的? 【参考方案1】:

我的意思是你可以使用INNER JOIN

UPDATE Request r                                              
INNER JOIN DailyData d                                              
    ON r.account = d.account AND r.subaccount = d.subaccount                                              
SET r.updatedAmount = d.amount
WHERE r.amount <> d.amount;

可以通过为两个表创建索引(帐户、子帐户)来提高更新性能

【讨论】:

(fwiw 在 mysql 上,LEFT join 莫名其妙地几乎总是比 INNER JOIN 快,还没有听到关于为什么的令人信服的理论,但它每次都赢得了基准测试) 原始查询中的 LEFT OUTER 连接可能是也可能不是错误(尤其是两个已发布查询之间的行为不同时)。但是,使用 INNER 连接会改变行为,因此应明确提及此类行为,以便使用正确的(按预期结果)行为。

以上是关于更新大型表上的行的最高效方法的主要内容,如果未能解决你的问题,请参考以下文章

在现有的大型表上创建列存储索引的最有效方法?

重写大型 IN 子句的最高效方法是啥?

连接表上的高效 GraphQL 查询

SQLAlchemy中所有行的高效更新

更新子表后数据库触发器更新父表上的行

基于日期时间值的相同表上的高效sql子查询