PL/SQL:是不是有完全停止脚本执行的指令?

Posted

技术标签:

【中文标题】PL/SQL:是不是有完全停止脚本执行的指令?【英文标题】:PL/SQL: is there an instruction to completely stop the script execution?PL/SQL:是否有完全停止脚本执行的指令? 【发布时间】:2012-01-18 09:58:16 【问题描述】:

我正在尝试在 PL/SQL 脚本的开头对数据库架构进行一些检查。

如果检查结果不成功,我想停止脚本,以防止执行下一条指令。

我得到了这样的东西

-- 1st line of PL/SQL script

DECLARE
  SOME_COUNT INTEGER;
BEGIN
  SELECT COUNT(*) INTO SOME_COUNT FROM SOME_TABLE WHERE <SOME_CONDITIONS>;
  IF (SOME_COUNT > 0) THEN
    DBMS_OUTPUT.PUT_LINE('Test failed, I don''want the rest of the script'
      || ' to be executed.');
    --EXIT or something like that?... <= STOP EXECUTION HERE
  END IF;
END;
/

-- OTHER SQL INSTRUCTIONS...
ALTER TABLE SOME_TABLE ...

我正在寻找允许执行“STOP EXECUTION HERE”的指令。

【问题讨论】:

您真的在谈论 PL/SQL 脚本吗?还是 SQLPlus 脚本?在 PL/SQL 中不能有 DDL 语句(除非你把它放在 EXECUTE IMMEDIATE 中)。所以在我看来,您是在谈论 SQLPlus 脚本。如果您在谈论 SQLPlus 脚本,如果 PL/SQL 块出错,SQLPlus 将默认继续执行脚本中的下一个 SQL 语句(或 PL/SQL 块)。您需要使用 SQL*Plus 命令WHENEVER SQLERROR EXIT 感谢贾斯汀的评论。事实上,即使我对 DBMS 有很好的了解,我也不是“Oracle 专家”。因此,我还没有掌握 PL/SQL 或 SQL*Plus 脚本之间的区别(你知道一些博客/文档/站点清楚地解释了这种区别吗?)。我只能说我正在使用 Navicat,使用“查询”窗格并将我的脚本文件加载到其中。在这种特定情况下,RAISE_APPLICATION_ERROR() 可以胜任。 【参考方案1】:

问题显示了一个多语句批处理脚本。 RAISE_APPLICATION_ERROR() 仅退出 PL/SQL 块(子程序),而不退出整个脚本(正如 Justin 所指出的),因此它将继续执行后面的语句。

对于批处理脚本,最好使用 WHENEVER SQLERROR EXIT。是的,它是一个 SQLPlus 指令,不是标准 SQL,但相当可移植;支持脚本的最流行的 Oracle 工具至少部分支持此指令。以下示例适用于 SQLPlus、SQL*Developer、Toad、SQLsmith 和其他可能的应用程序,如果您注释掉该行,则会演示该问题。

set serveroutput on

-- Without this line, things keep going
WHENEVER SQLERROR EXIT SQL.SQLCODE ROLLBACK;

BEGIN
  IF (1 > 0) THEN
    DBMS_OUTPUT.PUT_LINE('First thing');
    RAISE_APPLICATION_ERROR(-20000, 'Test failed'); -- not enough
  END IF;
END;
/

-- This will execute if you remove WHEN SQLERROR.., so RAISE_APPLICATION_ERROR is not enough
BEGIN
   DBMS_OUTPUT.PUT_LINE('Second thing - Executes anyway');
END;
/

如果您删除 WHEN SQLERROR,脚本将继续并执行第二个块等,这正是问题要求避免的。

在这种情况下,模拟 sqlplus 的图形工具的优势在于它们确实会停止脚本并且不会将脚本的其余部分作为 shell 命令提交到命令 shell,如果您粘贴脚本会发生这种情况进入在控制台窗口中运行的 SQLPlus。 SQLPlus 可能会因错误而退出,但剩余的缓冲命令将由 OS shell 处理,如果您在 cmets 中有 shell 命令(这并非闻所未闻),这有点混乱且可能存在风险.使用 SQLPlus,总是最好先连接,然后执行脚本,或者在 命令行参数中传递它(sqlplus scott/tiger @foo.sql)来避免这种情况。

【讨论】:

+1 这真的很有帮助。我知道异常和应用程序错误,但我需要在应该停止脚本的 DDL 脚本中进行一项前提条件检查。替代方案(在 PL/SQL 脚本的 IF 语句中执行 DDL)不是很诱人。 您可能想删除“不同意接受的答案”【参考方案2】:

如果您不想引发异常,可以尝试类似(未经测试):

declare
  SOME_COUNT INTEGER;
begin
  SELECT COUNT(*) INTO SOME_COUNT FROM SOME_TABLE WHERE <SOME_CONDITIONS>;
  IF (SOME_COUNT > 0) THEN
    DBMS_OUTPUT.PUT_LINE('Test failed, I don''want the rest of the script'
      || ' to be executed.');

    goto end_proc;
  END IF;

  -- A bunch of great code here

  <<end_proc>>
  null;  -- this could be a commit or other lines of code
end;

有些人讨厌任何 GOTO 语句,因为如果滥用它们可能会导致意大利面条式代码,但在这种简单的情况下(再次假设您不想引发异常)它们在 imo 中运行良好。

【讨论】:

谢谢,+1。我看到的唯一问题是,我有很多 DDL 语句(ALTER TABLE...)作为bunch of great code,需要大量使用EXECUTE IMMEDIATE 解决方法,因此脚本代码的可读性/可维护性会降低. 是的,那是与您的 OP 不同的问题,但我同意那里的一些代码远非“好”;-) 该评论有点讽刺,但重点是为您的问题提供替代解决方案。【参考方案3】:

再谷歌搜索几秒钟给了我答案:函数RAISE_APPLICATION_ERROR()

  IF (SOME_COUNT > 0) THEN
    RAISE_APPLICATION_ERROR(-20000, 'Test failed');
  END IF;

用户定义的错误代码应该在 -20000 和 -20999 之间。

有关 Oracle 文档的详细信息:http://docs.oracle.com/cd/B10501_01/appdev.920/a96624/07_errs.htm#877(定义您自己的错误消息:过程 RAISE_APPLICATION_ERROR 部分)

【讨论】:

如果您在问题中发布的代码中使用 raise_application_error,它将使您脱离 PL/SQL 块,但仍将执行 OTHER SQL INSTRUCTIONS 部分。这就是你所追求的吗?【参考方案4】:

与抛出应用程序错误相比,使用RETURN keyword 会简单得多,它可以非常顺利地退出当前的 PL/SQL 块。

只需确保在它之前执行DBMS_OUTPUT.PUT_LINE('Exited because &lt;error'),以向用户提供一个很好的信息,说明您为什么要退出!

【讨论】:

以上是关于PL/SQL:是不是有完全停止脚本执行的指令?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 SQLPlus 或 PL/SQL 中制作菜单?

完全用 PL/SQL 编写大型批处理程序是否很愚蠢?

使用 PL/SQL 执行 perl 脚本

PL/SQL数据开发那点事

异常后停止 PL/SQL 代码(PL/SQL、ORACLE)

PL/SQL 中的双“at”符号含义