如何在 Oracle 循环中的 select 语句中执行 alter 命令?

Posted

技术标签:

【中文标题】如何在 Oracle 循环中的 select 语句中执行 alter 命令?【英文标题】:How to execute alter command within select statement in a loop in Oracle? 【发布时间】:2020-10-24 13:16:58 【问题描述】:

我正在尝试通过脚本重建模式的索引,但我被困在通过 select 语句获取字符串 ALTER INDEX OWNER.INDEX_NAME REBUILD NOLOGGING 但我不知道如何执行更改命令的地方,请指南:

我尝试将第二个 for 循环中使用的 select 查询的值分配给 str,然后执行它,但它给出了错误。

IS
STR VARCHAR2(5000);
BEGIN

    FOR T IN (
     SELECT USERNAME FROM DBA_USERS WHERE USERNAME ='REPORT'
      )
     LOOP
     
     FOR CUR IN 
     (
       SELECT ' ALTER INDEX '||OWNER||'.'||INDEX_NAME|| ' REBUILD NOLOGGING; ' FROM DBA_INDEXES 
         WHERE  OWNER=T.USERNAME AND TEMPORARY='N'
       )
        
        LOOP 
       --- EXECUTE IMMEDIATE STR ;
       INSERT INTO INDEX_REBUILD_HISTORY 
         SELECT DISTINCT  OWNER, TRUNC(LAST_DDL_TIME) from DBA_OBJECTS where OBJECT_TYPE = 'INDEX' 
         AND 
         OWNER=T.USERNAME ;
         COMMIT;
    
         
    END LOOP;
    END LOOP;
    
    END ; 

【问题讨论】:

【参考方案1】:

您使用动态 sql。而且你不需要你的外循环。 dba_indexes 中提供了该过滤器:

create procedure bld_idx
is
vsql varchar2(500);

for x in (select owner,
                 index_name
          from dba_indexes
          where owner = 'REPORT'
          and TEMPORARY='N'
          )
loop
  vsql := ' ALTER INDEX '||x.OWNER||'.'||x.INDEX_NAME|| ' REBUILD NOLOGGING; ';
  dbms_output.put_line(vsql);  --  debugging only
  execute immediate vsql;
end loop;
end;

注 1:上面是我的头顶。可能存在轻微的语法问题,但如果是这样,您应该能够解决它们。

不是 2:重建索引不是正常情况下需要做的事情。 Richard Foote 可能是预言机索引内部最重要的权威,他有话要说:https://richardfoote.wordpress.com/2007/12/11/index-internals-rebuilding-the-truth/

【讨论】:

我写了你的脚本,但我收到 ORA-02243 错误:“无效的 ALTER INDEX 或 ALTER MATERIALIZED VIEW 选项” *原因:在 ALTER INDEX 中指定了除 INITRANS、MAXTRANS 或 STORAGE 以外的选项语句或 ALTER MATERIALIZED VIEW 语句的 USING INDEX 子句中。 *行动:仅指定合法选项。 好吧,我确实说过这不是我的想法,可能存在一些语法问题。去除那个 ';'从带引号的字符串内部构建 ALTER 语句。老实说,我也花了一点时间才发现它。执行动态 sql 时,我们不需要(或不想要!)终止分号作为字符串的一部分。 顺便说一句,我写了我之前的回复,以及我实际的编码答案,只是为了帮助你学习编写更好的 PL/SQL 我希望两次被引用的 Richard Foote 文章已经让你相信特定的程序是浪费时间。【参考方案2】:

“它给出了错误。”如果没有您收到的实际错误,将无济于事。那就是说你犯了很多其他人犯的同样的错误,你不应该包括“;”作为动态 SQL 的一部分 - 它不是语句的一部分,它仅由您的客户端用于了解何时将代码发送到数据库。

FOR CUR IN 
     (
       SELECT ' ALTER INDEX '||OWNER||'.'||INDEX_NAME|| ' REBUILD NOLOGGING' ddl_cmd FROM DBA_INDEXES 
         WHERE  OWNER=T.USERNAME AND TEMPORARY='N'
       )
...
EXECUTE IMMEDIATE CUR.ddl_cmd ;

(我还为该列指定了一个别名,以便您可以在循环中很好地使用它。

然后

  INSERT INTO INDEX_REBUILD_HISTORY 
         SELECT DISTINCT  OWNER, TRUNC(LAST_DDL_TIME) from DBA_OBJECTS where OBJECT_TYPE = 'INDEX' 
         AND 
         OWNER=T.USERNAME ;

没有过滤您刚刚重建的索引,它似乎不会获得完全有用的信息。

也就是说…… 离线重建所有索引并使其无法恢复真的值得吗?可能不会,如果您不止一次这样做并且从中受益,那么您的数据模型可能会有所改变以提供帮助。好好阅读 Richard Foote 的这篇演讲,他是一位知名的 Oracle 索引专家 https://richardfoote.files.wordpress.com/2007/12/index-internals-rebuilding-the-truth.pdf 我怀疑您是否会相信重建所有索引是一种解决方案。

【讨论】:

看来我们俩同时写了我们的回复!我希望我们都引用了相同的 Richard Foote 帖子这一事实不会在 OP 中丢失! :-)

以上是关于如何在 Oracle 循环中的 select 语句中执行 alter 命令?的主要内容,如果未能解决你的问题,请参考以下文章

在 SELECT 语句中更改 Oracle 中的列名

关于oracle循环语句的一个问题

如何从 oracle pl sql 中的包主体内的 select 语句中捕获特定列

从 Java 到 Oracle SQL 的翻译:将每个元素作为 SELECT 语句的结果使用到循环中[关闭]

oracle 隐式游标,显示游标,游标循环,动态SELECT语句和动态游标,异常处理,自定义异常

oracle SELECT语句太长的问题