如何在 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 命令?的主要内容,如果未能解决你的问题,请参考以下文章
如何从 oracle pl sql 中的包主体内的 select 语句中捕获特定列
从 Java 到 Oracle SQL 的翻译:将每个元素作为 SELECT 语句的结果使用到循环中[关闭]