我有两个表 - 一个用于处理导入数据的临时表和现有的帐户表。我需要根据帐户表中帐号和组号的匹配来更新临时表中的用户ID。
CREATE TABLE `_temp` (
`g_id` int(11) NOT NULL AUTO_INCREMENT,
`g_group_norm` varchar(10) DEFAULT NULL,
`g_uid1` varchar(25) DEFAULT NULL,
`g_spid` int(11) DEFAULT NULL,
`g_pid` int(11) DEFAULT NULL,
PRIMARY KEY (`g_id`),
KEY `groupn` (`g_group_norm`) USING BTREE,
KEY `guid` (`g_uid1`) USING BTREE,
KEY `gspid` (`g_spid`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
CREATE TABLE `accounts` (
`sa_actid` int(11) NOT NULL AUTO_INCREMENT,
`sa_grp` varchar(10) DEFAULT NULL,
`sa_account` varchar(25) DEFAULT NULL,
`sa_spid` int(11) DEFAULT NULL,
`sa_partnerid` int(11) DEFAULT NULL,
PRIMARY KEY (`sa_actid`),
KEY `spid` (`sa_spid`) USING BTREE,
KEY `grp` (`sa_grp`) USING BTREE,
KEY `act` (`sa_account`) USING BTREE,
KEY `partnerid` (`sa_partnerid`) USING BTREE,
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
帐户表格超过500万行。我现在使用的_temp表数据可以是50,000到700,000行。
我用来设置g_spid = sa_spid的查询是:
UPDATE _temp T, accounts A SET
T.g_spid = A.sa_spid
WHERE T.g_uid1 = A.sa_account
AND T.g_group_norm = A.sa_grp
AND A.sa_partnerid = 118
AND T.g_spid IS NULL;
帐户表有大约3M行,其中2.84M是合作伙伴ID" 118"。临时表更新工作缓慢,但在10-20k行上正常,但是当我有一组更大的数据要处理时(当前临时表大约是10万行)它似乎永远不会完成(它已经运行了现在15分钟。)
有更好的方法来执行此查询吗?我已经在g_uid1 = sa_account,g_group_norm = sa_grp等内部联接尝试了它,这似乎更慢。
答案 0 :(得分:1)
你可以在_temp上使用复合索引(g_group_norm,g_uid1,g_spid)
和帐户上的复合(sa_partnerid,sa_account,sa_spid)
UPDATE _temp T
INNER JOIN accounts A ON T.g_uid1 = A.sa_account
AND T.g_group_norm = A.sa_grp
AND A.sa_partnerid = 118
SET T.g_spid = A.sa_spid
WHERE T.g_spid IS NULL
答案 1 :(得分:1)
可以实现同等的结果:
UPDATE _temp t
SET t.g_spid = ( SELECT MIN(a.sa_spid)
FROM accounts a
WHERE a.sa_account = t.g_uid1
AND a.sa_grp = t.g_group_norm
AND a.sa_partnerid = 118
)
WHERE t.g_spid IS NULL
AND t.g_uid1 IS NOT NULL
AND t.g_group_norm IS NOT NULL
将为为外部查询返回的每个行执行相关子查询,因此对于性能,我们需要一个合适的索引,最好是覆盖索引。
使用相关子查询的WHERE子句中的相等条件,我们希望这三列在索引中排在第一位,最先选择最具选择性的列。 (帐户中有近95%的行的sa_partner_id值为118,这不是很有选择性,所以我们把它排在第三位。)
ON accounts (sa_account, sa_grp, sa_partner_id, sa_spid)
我们还包含sa_spid列,使其成为“覆盖”索引,以便可以完全从索引中满足子查询,而无需在基础表中查找页面。
(单例列上的索引可能对其他查询很有用,但它们不适合此特定查询。)
如果WHERE子句中的条件足够有选择性,我们也可以在_temp
表上添加索引。如果我们需要查看_temp
中超过10%或15%的行,则完整扫描可能会更快。
随着每一行的更新,维护g_spid
列上的索引会产生开销。对于大型集合,有时删除索引的速度更快,执行更新并重新添加索引。
(我怀疑_temp
表上的索引有更好的选择,但是如果不知道对表执行的其他SQL,就无法确定._temp表上的所有索引都看不到适用于此查询,除非外部查询的WHERE子句中的条件非常有选择性。)
对于_temp中的大量行,我们可能希望将操作分解为更小的集合。
使用相同的查询模式,但向外部查询添加另一个条件以将其分解为更小的集合。
作为示例(我不知道列的数据类型,值的范围或分布。正如该想法的说明,假设group_norm
是DECIMAL值,范围从0.00000到0.99999均匀分布,将UPDATE分成十个“集”......
首次运行
AND t.group_norm >= 0.0 AND t.group_norm < 0.1
第二次运行
AND t.group_norm >= 0.1 AND t.group_norm < 0.2
第三次运行
AND t.group_norm >= 0.2 AND t.group_norm < 0.3