【问题标题】:Optimizing SQL by using temporary table in OracleOracle中使用临时表优化SQL
【发布时间】:2017-12-31 00:39:25
【问题描述】:

我有一个数据清理程序,它从两个表的卡片行中删除相同的数据。 这两个更新语句都使用相同的子查询来检测应该更新哪些行。

 UPDATE table_1 SET card = NULL WHERE id in
               (select id from sub_table WHERE /* complex clause here */);

 UPDATE table_2 SET card = NULL WHERE id in
               (select id from sub_table WHERE /* complex clause here */);

使用 Oracle 临时表是优化我的代码的好方法吗?

CREATE TEMPORARY TABLE tmp_sub_table AS
select id from sub_table WHERE /* complex clause here */;

UPDATE table_1 SET card = NULL WHERE id in (select * from tmp_sub_table);    
UPDATE table_2 SET card = NULL WHERE id in (select * from tmp_sub_table);

我应该使用本地临时表还是全局临时表?

【问题讨论】:

  • 如果sub_table中的数据很大,我建议使用全局临时表。
  • 根据您的第二个示例,我第二次选择全局临时表 (GTT)(尽管我会将 select * from tmp_sub_table 更改为 select id from tmp_sub_table 以避免出现问题,如果有人在未来)。 GTT 的好处是您只需创建一次,因此您无需在代码中创建和删除它。

标签: oracle performance optimization temp-tables


【解决方案1】:

全局临时表是持久性数据结构。当我们 INSERT 将数据写入磁盘时,当我们 SELECT 时从磁盘读取数据。所以磁盘 I/O 相当多:运行相同查询两次所节省的成本必须大于所有这些写入和读取的成本。

需要注意的一点是,GTT 是建立在临时表空间之上的,因此您可能会与其他正在执行排序等长时间运行的进程发生争用。最好有一个单独的临时表空间,仅用于 GTT但这样做的 DBA 并不多。

另一种解决方案是使用集合将记录的子集存储在内存中并使用批量处理。

 declare
     l_ids sys.ocinumberlist;
     cursor l_cur is
         select id from sub_table WHERE /* complex clause here */
         order by id
         ;
begin
    open lcur;
    loop
        fetch lcur bulk collect into l_ids limit 5000;
        exit when l_ids.count() = 0;

        update table1 
        set card=null
        where id member of l_ids;

        update table2 
        set card=null
        where id member of l_ids;

    end loop;
end;

“使用一条更新语句更新多行...比使用 Looping over cursor 单独更新要快得多”

这是正常的建议。但这是一个批量操作:它一次更新五千行,所以它比逐行更快。批处理的大小由BULK COLLECT ... LIMIT 子句控制:您不希望将值设置得太高,因为集合位于 会话内存 中,但您只选择一列 - 并且一个数字——也许你可以让它更高。

与往常一样,调整是基准测试的问题。您是否确定运行此子查询两次是一项高成本操作?

select id from sub_table WHERE /* complex clause here */

如果它看起来太慢,您需要测试其他方法,看看它们是否更快。也许全局临时表比批量操作更快。通常,内存访问比磁盘访问快,但您需要查看哪种方式最适合您。

【讨论】:

  • 在这种情况下,我必须在单独的语句中更新我的所有数据,这会降低性能。
  • 使用一条更新语句更新多行:“UPDATE table_1 SET card = NULL WHERE id in (select id from sub_table WHERE);”比使用 Looping over cursor 单独更新要快得多。
  • @mariami APC 的解决方案是一次获取 5000 行,然后根据这 5000 行的 id 对两个表进行更新。它的性能可能不如 GTT 解决方案,也可能不如 GTT 解决方案——您应该测试这两种方法,看看哪种方法最适合您。
猜你喜欢
  • 2010-11-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-31
  • 2018-04-07
  • 1970-01-01
  • 1970-01-01
  • 2016-02-29
相关资源
最近更新 更多