【发布时间】:2018-10-22 02:45:22
【问题描述】:
目前,我正在 PL/SQL Oracle 12.1 中运行以下单个更新,并且需要知道如何通过批量更新来提高性能,因为它们需要几个小时才能完成,或者任何其他策略。
问题是我需要从有几十万条记录(MAIN_TBL 本身有几百条记录的表 (LARGE_TBL) 中更新具有一组相同条件(那些 CASE WHEN 语句)的多个列也有数千条记录。两个表都有LT_ID 和MT_ID 的索引。
还有多个其他UPDATES 具有不同的LT.IDX_2 和MT.IDX_2 值(为简洁起见,我排除了它们)并且只显示IDX_2 = G(还有其他相同的UPDATE 与其他IDX_2值)。
UPDATE MAIN_TBL MT
SET
MT.STOP_FLAG = (
SELECT
CASE
WHEN
NOT EXISTS (SELECT 1 FROM LARGE_TBL LT WHERE LT.LT_ID = MT.MT_ID AND LT.IDX_2 = 'G')
OR (SELECT LT.COL_1 FROM LARGE_TBL LT WHERE LT.LT_ID = MT.MT_ID AND LT.IDX_2 = 'G') IS NULL
OR (SELECT LT.COL_1 FROM LARGE_TBL LT WHERE LT.LT_ID = MT.MT_ID AND LT.IDX_2 = 'G') <> 'Y'
THEN 'SF01'
ELSE MT.STOP_FLAG
END
FROM DUAL
),
MT.ES = (
SELECT
CASE
WHEN
NOT EXISTS (SELECT 1 FROM LARGE_TBL LT WHERE LT.LT_ID = MT.MT_ID AND LT.IDX_2 = 'G')
OR (SELECT LT.COL_1 FROM LARGE_TBL LT WHERE LT.LT_ID = MT.MT_ID AND LT.IDX_2 = 'G') IS NULL
OR (SELECT LT.COL_1 FROM LARGE_TBL LT WHERE LT.LT_ID = MT.MT_ID AND LT.IDX_2 = 'G') <> 'Y'
THEN 'E'
ELSE MT.ES
END
FROM DUAL
),
MT.PW = (
SELECT
CASE
WHEN
NOT EXISTS (SELECT 1 FROM LARGE_TBL LT WHERE LT.LT_ID = MT.MT_ID AND LT.IDX_2 = 'G')
OR (SELECT LT.COL_1 FROM LARGE_TBL LT WHERE LT.LT_ID = MT.MT_ID AND LT.IDX_2 = 'G') IS NULL
OR (SELECT LT.COL_1 FROM LARGE_TBL LT WHERE LT.LT_ID = MT.MT_ID AND LT.IDX_2 = 'G') <> 'Y'
THEN 'W'
ELSE MT.PW
END
FROM DUAL
),
MT.UPDATE_DT = SYSDATE
WHERE
MT.STOP_FLAG IS NULL
AND MT.IDX_2 = 'G'
AND MT.ES IS NULL
AND MT.SS = 'C'
AND MT.PW = 'A';
UPDATE MAIN_TBL MT
SET
MT.STOP_FLAG = (
SELECT
CASE
WHEN
NOT EXISTS (SELECT 1 FROM LARGE_TBL LT WHERE LT.LT_ID = MT.MT_ID AND LT.IDX_2 = 'G')
OR (SELECT LT.COL_2 FROM LARGE_TBL LT WHERE LT.LT_ID = MT.MT_ID AND LT.IDX_2 = 'G') IS NULL
OR (SELECT LT.COL_2 FROM LARGE_TBL LT WHERE LT.LT_ID = MT.MT_ID AND LT.IDX_2 = 'G') <> 'Y'
THEN 'SF02'
ELSE MT.STOP_FLAG
END
FROM DUAL
),
MT.ES = (
SELECT
CASE
WHEN
NOT EXISTS (SELECT 1 FROM LARGE_TBL LT WHERE LT.LT_ID = MT.MT_ID AND LT.IDX_2 = 'G')
OR (SELECT LT.COL_2 FROM LARGE_TBL LT WHERE LT.LT_ID = MT.MT_ID AND LT.IDX_2 = 'G') IS NULL
OR (SELECT LT.COL_2 FROM LARGE_TBL LT WHERE LT.LT_ID = MT.MT_ID AND LT.IDX_2 = 'G') <> 'Y'
THEN 'E'
ELSE MT.ES
END
FROM DUAL
),
MT.PW = (
SELECT
CASE
WHEN
NOT EXISTS (SELECT 1 FROM LARGE_TBL LT WHERE LT.LT_ID = MT.MT_ID AND LT.IDX_2 = 'G')
OR (SELECT LT.COL_2 FROM LARGE_TBL LT WHERE LT.LT_ID = MT.MT_ID AND LT.IDX_2 = 'G') IS NULL
OR (SELECT LT.COL_2 FROM LARGE_TBL LT WHERE LT.LT_ID = MT.MT_ID AND LT.IDX_2 = 'G') <> 'Y'
THEN 'W'
ELSE MT.PW
END
FROM DUAL
),
MT.UPDATE_DT = SYSDATE
WHERE
MT.STOP_FLAG IS NULL
AND MT.IDX_2 = 'G'
AND MT.ES IS NULL
AND MT.SS = 'C'
AND MT.PW = 'A';
问题在于,例如在上述情况下,第二个UPDATE 依赖于第一个UPDATE,因为第二个UPDATE 只有在MT.STOP_FLAG IS NULL 时才会执行。因此,如果MT.STOP_FLAG 设置为第一个UPDATE(使用MT.STOP_FLAG = SF01),则不应执行第二个UPDATE,因为不会满足WHERE 子句(MT.STOP_FLAG IS NULL)。换句话说,这些UPDATE 的执行顺序很重要。
我没有使用 PL/SQL 的批量更新功能,所以我不知道如何处理这个问题。我是否应该创建一个游标以从 LARGE_TBL 表中获取所有必要的列,并具有适当的条件,例如IDX_2 = G或IDX_2 = R,这是一个比较大的表(几十万条记录),然后用BULK COLLECT将它们取到几个定义的TYPE中,最后使用单个FORALL和多个单独的@ 987654351@ 声明?还是多个FORALL,每个UPDATE一个?
如果要使用游标,我如何处理我的CASE WHEN 语句中的第一项,我需要确保是否存在记录?
【问题讨论】:
-
请通读链接并尝试创建要更新的程序。如果您遇到任何困难,请联系我们。 blogs.oracle.com/oraclemagazine/…
-
您的
CASE表达式看起来很奇怪。你选择同一张桌子9次,你怎么能期待好的表现?尝试只选择表 LARGE_TBL 一次。
标签: oracle plsql sql-update oracle12c database-performance