我有一张包含500万条记录的表mytable(**id**,colA,colB)
。 colA,colB没有其他限制。我必须将colB中的值复制到colA并使colB为null。这是我在下面创建的程序。有时它会在5分钟内运行,有时需要45分钟。这个脚本有什么问题?我确信在此期间没有其他进程访问此表。我该如何优化? (我知道会有很多其他因素会影响速度,就像数据库引擎本身运行速度慢,可能就是机器当时正在满负荷运行。我正在寻找我手中的东西,即我的脚本。 )
DECLARE
l_update_total pls_integer := 0;
CURSOR cur IS SELECT id, colB FROM mytable where colA is null;
TYPE t_recs IS TABLE OF cur%ROWTYPE;
l_loop_count pls_integer := 0;
l_recs t_recs;
l_rec cur%ROWTYPE;
BEGIN
OPEN cur;
LOOP
FETCH cur BULK COLLECT INTO l_recs LIMIT 500;
EXIT WHEN l_recs.COUNT = 0;
FOR indx IN 1 .. l_recs.COUNT
LOOP
l_rec := l_recs(indx);
UPDATE mytable SET colB=null,colA = l_rec.colB WHERE id = l_rec.id;
l_update_total := l_update_total + SQL%ROWCOUNT;
END LOOP;
COMMIT;
END LOOP;
END;
/
答案 0 :(得分:4)
转储游标和过程逻辑,并将其重写为SQL。
答案 1 :(得分:4)
为什么不编写简单的SQL语句
UPDATE mytable
SET colB = null,
colA = colB
WHERE colA IS NULL;
纯SQL解决方案将比PL / SQL解决方案更快。
但是,如果您不想了解运行时间变化的原因,那么提高性能将会非常困难。如果系统如此重载,它会为您的过程的运行时增加40分钟,而这些都不是由于锁定(因为您声称没有其他会话正在读取或写入相关表),所以完全有可能它将为单个SQL语句的运行时添加40分钟。
如果你坚持使用较慢的PL / SQL方法,至少取出循环中的commit
以避免每批产生这种开销。
答案 2 :(得分:1)
数据库查询并不意味着逐行进行(RBAR,对于Jeff Moden粉丝)。考虑一组数据,而不是行。
从技术上讲,简单的更新会更好:
UPDATE mytable SET colB=null, colA=colB WHERE colA is null
由于您似乎确信在操作期间没有其他进程正在触及此表,因此锁定不应成为问题。但是,可用资源很可能是(考虑到记录的数量)。
如果在单个查询中执行操作在服务器上过于苛刻,请尝试使用混合解决方案,每次更新几千行,每次使用一个UPDATE语句。我的PL / SQL日子很远,从那时起就有很多T-SQL,所以我现在无法给你精确的合成器,但这个想法仍然是一样的。