【问题标题】:PL/SQL Package invalidatedPL/SQL 包失效
【发布时间】:2011-01-30 22:58:10
【问题描述】:

我有一个使用包 (PKG_MY_PACKAGE) 的脚本。我将更改该包中查询中的一些字段,然后重新编译它(我不更改或编译任何其他包)。我运行脚本,我得到一个看起来像

的错误 ORA-04068: 包的现有状态已被丢弃 ORA-04061: 包主体 "USER3.PKG_MY_PACKAGE" 的现有状态已失效 ORA-04065: 未执行、更改或删除包主体 "USER3.PKG_MY_PACKAGE" ORA-06508: PL/SQL: 找不到被调用的程序单元: "USER3.PKG_MY_PACKAGE" ORA-06512: 在第 34 行

我再次运行脚本(不更改系统中的任何其他内容)并且脚本成功执行。

我认为当我在执行脚本之前编译时会修复任何无效引用。这是 100% 可重现的,我使用这个脚本的次数越多,它就越烦人。什么可能导致这种情况,什么可以解决它?

(oracle 10g,使用 PL/SQL Developer 7)

【问题讨论】:

  • @OMG:我只是重新编译正文,因为这就是所有的变化。
  • 没有理由为此使用全局变量 - 只需切换到 sys_context。这样您就可以完全删除全局变量并使您的包无状态。

标签: oracle plsql


【解决方案1】:

您可能遇到的问题是:

  • 您调用的程序包/程序无效(但如果单独调用它可以工作) 检查此查询是否在此 all_objects 视图中包含您的包或包中使用的对象
select * from all_objects where status = 'INVALID' and owner = 'SCHEMA_NAME';
  • 检查你的包是否有全局变量?如果是,则检查这些变量是否没有被任何其他会话更改,最好删除这些全局变量/使用函数

  • 运行以下脚本以编译架构中的所有对象

begin
   dbms_utility.compile_schema('SCHEMA_NAME', false); 
end;
  • 如果上述方法均无效,则最后一个选项从您的包中删除所有过程/函数,添加新函数并尝试从触发器运行您的函数。检查这是否有效,然后您的包裹处于特殊锁定状态。添加新的函数/过程后,它的状态将再次有效,然后您可以添加所有实际的函数/过程并删除新添加的函数/过程。

【讨论】:

    【解决方案2】:

    上述错误:ORA-06508: PL/SQL: 找不到被调用的程序单元。

    是在尝试调用无法找到的存储程序时引起的。该程序可能已被删除或修改不兼容,或已编译错误。

    检查所有引用的程序,包括它们的包体,是否存在并且兼容。

    您可以运行此查询来查找无效对象,这可能会导致 ORA-06508 错误:

    选择 comp_id, comp_name, 版本, 状态, 命名空间, 图式 从 dba_registry;

    【讨论】:

    • 这看起来像是直接从 Oracle 文档网页复制的。
    【解决方案3】:

    背景

    existing state of packages has been discarded 表示你的包有某种状态。

    这是由存储在包主体中的全局变量引起的。
    在 11.2.0.2 之前,常量也会导致这种行为(请参阅 documentation)。

    由于包已在您的会话中使用,Oracle 假定此状态与您相关。其中一些变量现在可能具有不同的值,当您重新编译 Body 时,这些值会被重置。

    抛出此异常,以便您的客户知道他们不能再依赖这些变量。

    解决方案

    • 如果可能,从包体中删除所有全局变量和常量(11gR2 之前)
    • DETERMINISTIC 函数替换全局变量(如this answer 建议的那样)
    • 使用 PRAGMA SERIALLY_REUSABLE 定义包会导致 Oracle 在每次调用服务器时重新初始化全局变量。
    • 关闭会话并重新连接,然后再次调用包。
    • 手动重置状态(参见Paul James' answer

    【讨论】:

    • 确实,我确实有一些全局变量!我会看看我是否能找到解决方法,也许让它们成为非全局变量......有没有办法让 PL/SQL 中的过程具有静态值?
    • “过程的静态值”到底是什么意思?这些全局变量或多或少是静态变量(每个会话)。对您来说最简单的解决方法是将全局变量放在包头而不是正文中 - 编译正文时状态将不再无效。为什么需要这些全局变量?
    • 它们持有只需要设置一次的配置值并驱动系统的其余部分。我不想将它们放入标题中,因为我不希望任何其他代码可以访问它们(它们从脚本传递到一个相关的过程中)。我想它们可以存储在一个表中,但如果我这样做,该表上可能会有很多读取。
    • 猜猜我也会将配置放入正文中。从表格中读取它可能会减慢您的过程。您将来是否仍要重新编译您的 Body,还是现在仅用于测试?另一种方法是定义一个包含所有必要变量的RECORD,并将其传递给您调用的过程。
    • 我想现在我只会从表格中读取。我已经重写了部分受影响的代码,所以我认为它不会有太多的读取,所以现在可能不是主要的性能问题。我只是为开发/测试进行所有这些重新编译,所以这在生产系统中应该不是问题。
    【解决方案4】:

    如果您在脚本中运行内容,请在运行重新编译的代码之前尝试其中的这些命令..

    exec DBMS_SESSION.RESET_PACKAGE
    exec DBMS_SESSION.MODIFY_PACKAGE_STATE( DBMS_SESSION.REINITIALIZE )
    

    他们会按照名字所暗示的那样去做。

    【讨论】:

    • 有趣...我得试试这个。
    • 伙计,我希望我可以将两个答案标记为已接受的答案。这里的其他开发人员之一说这很好,这意味着我不必担心重写和重新测试代码。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-03
    • 1970-01-01
    • 1970-01-01
    • 2021-05-09
    相关资源
    最近更新 更多