如何在 Oracle SQL 中优化或在没有循环的情况下执行此操作

Posted

技术标签:

【中文标题】如何在 Oracle SQL 中优化或在没有循环的情况下执行此操作【英文标题】:How to optimize this or do this without a loop in Oracle SQL 【发布时间】:2020-03-29 02:42:34 【问题描述】:

我有下面的代码,这个代码应该告诉我们给定表有多少数据块没有一行。我有 15 秒的时间限制,但是当我第一次运行它时,它需要 103 秒才能运行。我试图通过消除循环进行优化,但我做不到,我什至想不出没有循环的解决方案。

create or replace procedure num_of_rows IS
 cnt NUMBER;
 total NUMBER;
BEGIN 
 total := 0;
 FOR rec IN (select file_id, block_id, blocks from dba_extents 
             where owner='TABLE_OWNER' and segment_name='TABLE_NAME')
 LOOP
  FOR i in 1..rec.blocks LOOP
   SELECT count(*) into cnt FROM TABLE_OWNER.TABLE_NAME 
   WHERE dbms_rowid.rowid_relative_fno(ROWID) = rec.file_id
   AND dbms_rowid.rowid_block_number(ROWID) = rec.block_id+i-1;
   IF cnt = 0 THEN total := total + 1; END IF;
  END LOOP;
 END LOOP;
 dbms_output.put_line(total);
END num_of_rows;



set serveroutput on
execute num_of_rows();

能否请您告诉我可以优化的方法? 在此先感谢:)

【问题讨论】:

我可以,但这不是您需要考虑的重点吗? 【参考方案1】:

这只扫描表一次,所以应该更快。

它通过为dba_extents 中的每一行生成一个虚拟行集来生成每个block_idcross apply 需要 Oracle 12.1),然后排除表中存在的块。

select count(*)
from   dba_extents x
       cross apply (select x.block_id + rownum - 1 as block_id from dual connect by rownum <= x.blocks) x2
where  owner = 'TABLE_OWNER'
and    segment_name = 'TABLE_NAME'
and    ( x.relative_fno, x2.block_id )
       not in
       ( select distinct dbms_rowid.rowid_relative_fno(rowid) as rowid_relative_fno
              , dbms_rowid.rowid_block_number(rowid) as rowid_block_number
         from  table_owner.table_name );

【讨论】:

【参考方案2】:

如果您可以使用动态 SQL,那就去吧。您可以通过将 2FORLOOP-2SELECT 重写为单个查询来实现,这将提高性能。

【讨论】:

欢迎来到 SO!请提供重写示例,以便 OP 和其他人可以看到您的意思。请参阅此处以供参考:***.com/help/how-to-answer 不幸的是我不能使用动态 sql

以上是关于如何在 Oracle SQL 中优化或在没有循环的情况下执行此操作的主要内容,如果未能解决你的问题,请参考以下文章

如何强制优化器重用 sql 游标

如何对Oracle sql 进行性能优化的调整

经典案例:如何优化Oracle使用DBlink的SQL语句

Oracle sql优化示例

oracle存储过程中循环调用存储过程

Oracle PL/SQL - 循环值作为没有动态 SQL 的动态列名