ORACLE DB 优化更新查询以根据其他表中的数据更新列中的所有空值

问题描述 投票:0回答:2

我最近在 ORACLE DB 中制作了这个脚本来更新我刚刚创建的列上的所有现有值。但是我被告知存在函数非常慢,我应该使用连接更新。不幸的是,我无法找到有用的示例和/或有关如何在 ORACLE DB 中正确执行此操作的信息。

UPDATE appeal app
SET app.is_initial = (CASE WHEN EXISTS(SELECT 1
                                             FROM appeal app_sub
                                                      JOIN event_person ep ON app_sub.id = ep.appeal_id
                                                      JOIN event ON ep.event_id = event.id
                                             WHERE name_code = 'INITIAL' AND app.id = app_sub.id)
                                     THEN 1 ELSE 0 END)
WHERE app.is_initial IS NULL;

如果有人知道如何做到这一点,我将非常感谢您的帮助。附言。为了安全起见,我更改了表的名称。

sql oracle query-optimization
2个回答
0
投票

假设

name_code
event
event_person
表中,那么您似乎不需要在子查询中使用
APPEAL
,可以将其简化为:

UPDATE appeal app
SET app.is_initial = (SELECT LEAST(COUNT(*), 1)
                      FROM   event_person ep
                             INNER JOIN event
                             ON ep.event_id = event.id
                      WHERE  name_code = 'INITIAL'
                      AND    app.id = ep.appeal_id)
WHERE app.is_initial IS NULL;

或:

UPDATE appeal app
SET app.is_initial = COALESCE(
                       ( SELECT 1
                         FROM   event_person ep
                                INNER JOIN event
                                ON ep.event_id = event.id
                         WHERE  name_code = 'INITIAL'
                         AND    app.id = ep.appeal_id
                         AND    ROWNUM = 1 ),
                       0
                     )
WHERE app.is_initial IS NULL;

小提琴


0
投票

EXISTS
不一定很慢,但可以。以下是一些需要检查的事项:

您有

app.id
event_person.appeal_id
event.id
的索引吗?除非 Oracle 重写它,否则标准
EXISTS
子查询将使用嵌套循环,并且您需要对连接列进行正确索引,以便其性能良好。只需确保这些索引就位就可能解决您的问题,而无需重写 SQL。如果您的数据集很小,我建议您这样做。

但是,通过使用

MERGE
,您通常可以为非常大的卷获得更好的性能,这使您能够使用更高效的并行和直接路径扫描以及散列连接来组装数据以驱动更新。在大多数情况下,它通常“不”使用索引。它看起来像这样(过度暗示只是为了表达观点(并且是确定的),这些暗示中的大多数可能是不必要的)。 MERGE /*+ USE_HASH(src app) */ INTO appeal app USING (SELECT /*+ parallel(8) NO_MERGE USE_HASH(app_sub ep event) */ app_sub.id, MAX(DECODE(event.id,NULL,0,1)) at_least_one FROM appeal app_sub LEFT OUTER JOIN event_person ep ON app_sub.id = ep.appeal_id LEFT OUTER JOIN event ON ep.event_id = event.id WHERE name_code = 'INITIAL' AND app_sub.is_initial IS NULL GROUP BY app_sub.id) src ON (src.id = app.id) WHEN MATCHED THEN UPDATE SET app.is_initial = src.at_least_one

(我不知道
name_code

来自哪个表;确保它

没有
索引,或者如果有,请确保Oracle没有使用该索引并添加FULL([table])提示以防止它这样做,如果是的。)
最后,对于非常大的卷,最佳方法是根本不更新,而是创建一个新表并替换旧表:

CREATE TABLE appeal2 parallel (degree 8) AS SELECT /*+ USE_HASH(app epx) */ app.id, app.col1, app.col2, app.col3, NVL(app.is_initial,DECODE(apx.appeal_id,NULL,0,1)) is_initial FROM appeal app LEFT OUTER JOIN (SELECT /*+ NO_MERGE USE_HASH(ep event) */ ep.appeal_id, FROM event_person ep INNER JOIN event ON ep.event_id = event.id WHERE name_code = 'INITIAL' GROUP BY ep.appeal_id) epx ON epx.appeal_id = app.appeal_id; ALTER TABLE appeal RENAME TO appeal_old; ALTER TABLE appeal2 RENAME TO appeal; -- also rebuild and indexes, constraints and reissue grants.

创建新段总是比更新表的每一行更快、更高效。如果表很小,则不值得执行额外的步骤,但如果您谈论的是数亿或数十亿行,那么这是最高效的方法。

© www.soinside.com 2019 - 2024. All rights reserved.