耗时 SQL 更新语句
Time Consuming SQL Update Statement
在 Postgresql(版本 9.2)中,我需要用另一个 table 的值更新一个 table。下面的 UPDATE 语句适用于小型数据集(1K 记录)并快速完成。记录量大(600K+),超过两个小时语句还没有完成。不知道是时间长了还是挂了。
UPDATE training_records r SET cid =
(SELECT cid_main FROM account_events e
WHERE e.user_ekey = r.ekey
AND e.type = 't'
AND r.enroll_date < e.time
ORDER BY e.time ASC LIMIT 1)
WHERE r.cid IS NULL;
这个说法有问题吗?有没有更有效的方法来做到这一点?
关于操作: training_records
保存分组的会员帐户(id by ekey
)的课程注册记录。 cid
是群号。 account_events
保存帐户更改事件,包括组之间的转移 (e.type='t'
),其中 cid_main
将是转移前的组 ID。我正在尝试追溯修补 training_records
中新添加的 cid
列,以便它准确反映课程注册时的组成员身份。可能有多次转移,所以我从注册后最早的转移中选择组 ID (cid_main
)。希望这是有道理的。
table training_records
有近 700K 条记录,account_events
有 560K+ 条记录。
EXPLAIN {以上命令}的输出
Update on training_records r (cost=0.00..13275775666.76 rows=664913 width=74)
-> Seq Scan on training_records r (cost=0.00..13275775666.76 rows=664913 width=74)
Filter: (cid IS NULL)
SubPlan 1
-> Limit (cost=19966.15..19966.16 rows=1 width=12)
-> Sort (cost=19966.15..19966.16 rows=1 width=12)
Sort Key: e."time"
-> Seq Scan on account_events e (cost=0.00..19966.15 rows=1 width=12)
Filter: ((r.enroll_date < "time") AND (user_ekey = r.ekey) AND (type = 't'::bpchar))
(9 rows)
再更新一次:
在 WHERE 中添加一个附加条件,我将记录数从 training_records
限制在 10K 左右。更新大约需要 15 分钟。如果时间与这个 table 的记录数接近线性关系,700K 条记录大约需要 17 个小时以上。
感谢您的帮助!
更新:花了将近9个小时,但是原来的命令完成了。
尝试将其转换为不强制嵌套循环连接的内容:
UPDATE training_records r
SET cid = e.cid_main
FROM account_events e
WHERE e.user_ekey = r.ekey
AND e.type = 't'
AND r.enroll_date < e.time
AND NOT EXISTS (SELECT 1 FROM account_events e1
WHERE e1.user_ekey = r.ekey
AND e1.type = 't'
AND r.enroll_date < e1.time
AND e1.time < e.time)
AND r.cid IS NULL;
该语句实际上并不等效:如果没有匹配的 account_events
行,您的语句会将 cid
更新为 NULL,而我的语句不会更新该行。
在 Postgresql(版本 9.2)中,我需要用另一个 table 的值更新一个 table。下面的 UPDATE 语句适用于小型数据集(1K 记录)并快速完成。记录量大(600K+),超过两个小时语句还没有完成。不知道是时间长了还是挂了。
UPDATE training_records r SET cid =
(SELECT cid_main FROM account_events e
WHERE e.user_ekey = r.ekey
AND e.type = 't'
AND r.enroll_date < e.time
ORDER BY e.time ASC LIMIT 1)
WHERE r.cid IS NULL;
这个说法有问题吗?有没有更有效的方法来做到这一点?
关于操作: training_records
保存分组的会员帐户(id by ekey
)的课程注册记录。 cid
是群号。 account_events
保存帐户更改事件,包括组之间的转移 (e.type='t'
),其中 cid_main
将是转移前的组 ID。我正在尝试追溯修补 training_records
中新添加的 cid
列,以便它准确反映课程注册时的组成员身份。可能有多次转移,所以我从注册后最早的转移中选择组 ID (cid_main
)。希望这是有道理的。
table training_records
有近 700K 条记录,account_events
有 560K+ 条记录。
EXPLAIN {以上命令}的输出
Update on training_records r (cost=0.00..13275775666.76 rows=664913 width=74)
-> Seq Scan on training_records r (cost=0.00..13275775666.76 rows=664913 width=74)
Filter: (cid IS NULL)
SubPlan 1
-> Limit (cost=19966.15..19966.16 rows=1 width=12)
-> Sort (cost=19966.15..19966.16 rows=1 width=12)
Sort Key: e."time"
-> Seq Scan on account_events e (cost=0.00..19966.15 rows=1 width=12)
Filter: ((r.enroll_date < "time") AND (user_ekey = r.ekey) AND (type = 't'::bpchar))
(9 rows)
再更新一次:
在 WHERE 中添加一个附加条件,我将记录数从 training_records
限制在 10K 左右。更新大约需要 15 分钟。如果时间与这个 table 的记录数接近线性关系,700K 条记录大约需要 17 个小时以上。
感谢您的帮助!
更新:花了将近9个小时,但是原来的命令完成了。
尝试将其转换为不强制嵌套循环连接的内容:
UPDATE training_records r
SET cid = e.cid_main
FROM account_events e
WHERE e.user_ekey = r.ekey
AND e.type = 't'
AND r.enroll_date < e.time
AND NOT EXISTS (SELECT 1 FROM account_events e1
WHERE e1.user_ekey = r.ekey
AND e1.type = 't'
AND r.enroll_date < e1.time
AND e1.time < e.time)
AND r.cid IS NULL;
该语句实际上并不等效:如果没有匹配的 account_events
行,您的语句会将 cid
更新为 NULL,而我的语句不会更新该行。