【问题标题】:Regarding Oracle Stored Procedure not compiling because table does not currently exist关于由于表当前不存在而无法编译的 Oracle 存储过程
【发布时间】:2010-06-30 17:31:23
【问题描述】:

全部,

我有以下包装说明:

CREATE OR REPLACE PACKAGE ashish.PKG_Customer AUTHID CURRENT_USER AS
  TYPE cursorType IS REF CURSOR;

  PROCEDURE CreateCustomerTable;
  PROCEDURE SelectCustomers(o_ResultSet OUT cursorType);    
END PKG_Customer;

这是包体:

CREATE OR REPLACE PACKAGE BODY ashish.PKG_Customer AS

  PROCEDURE CreateCustomerTable AS
    sQuery VARCHAR2(1000);
  BEGIN
    sQuery := 'CREATE TABLE tblCustomer2(
               CustomerID INTEGER PRIMARY KEY,
               FirstName VARCHAR2(50),
               LastName VARCHAR2(50),
               City VARCHAR2(200), 
               State_Province VARCHAR2(100),
               PostalCode VARCHAR2(25)
              )';
    EXECUTE IMMEDIATE sQuery;
  END CreateCustomerTable;

  PROCEDURE SelectCustomers(o_ResultSet OUT cursorType) AS
  BEGIN
    OPEN o_ResultSet FOR
      SELECT CustomerID,
             FirstName,
             LastName,
             City,
             State_Province,
             PostalCode
        FROM tblCustomer;
  END SelectCustomers;
END PKG_Customer;

我面临的问题是我的包无法编译,因为该表当前不存在。当然,我应该能够提前为 Oracle 中当前不存在的表创建存储过程,对吧?我在这里做错了吗?

服务器版本是 Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bit。

谢谢!
-阿什

【问题讨论】:

  • 过去,当我发布一个问题,人们认为出于某种原因需要编辑该问题时,有人问我相反的问题:为什么不是这个社区 wiki?所以从那以后,我只检查社区维基上的所有问题。我很想知道什么时候应该或不应该成为社区 wiki 的问题。
  • 据我了解,wiki 是解决没有“灵丹妙药”答案的问题的好地方(编程风格、最佳实践、个人偏好、您编写的第一个程序等可以讨论的主题断断续续 - 永远不会得出可以真正成为问题“可接受”解决方案的结论)。
  • 谢谢@eyescream。我会更加小心地区分何时应该和不应该选中社区 wiki 选项。

标签: sql oracle stored-procedures oracle10g


【解决方案1】:

我不知道。如何针对不存在的对象编译一些东西? Oracle 不知道您是在尝试引用现有表时输入错误的表名还是希望稍后再创建该表。

为什么不先创建表然后再创建/编译包?

【讨论】:

  • 我正在尝试将内部工具的内联 SQL 移植到 Oracle 存储过程中。该工具在转换数据和执行计算时创建了许多表。所以会有很多存储过程只是创建表,还有一些将从这些表中检索数据。存储过程使用的表在创建存储过程之前存在的期望似乎有点限制,不是吗?
  • 我猜你可能会争辩说它是有限制的,但同时编译的想法是检查语法并确保事情能按所写的那样工作。我不推荐解决此问题的一种方法是使用 EXECUTE IMMEDIATE。例如: EXECUTE IMMEDIATE 'SELECT x, y FROM table_which_is_hopefully_here WHERE blah = ?'使用 myBlahVariable;在不知道您的确切用例的情况下,临时表会有所帮助吗?
  • 对于一些中间表,我本来打算使用 GLOBAL TEMPORARY 表,但我觉得这种情况几乎是必要的。看来我必须走那条路并为我想要保留的表创建空壳。谢谢!
  • 一般来说,您不应该在 PL/SQL 中创建和删除表。 Oracle 可以更好地处理常量数据结构(以及适当收集的统计信息)。
【解决方案2】:

你可以提前创建包(意味着它将作为一个对象存在于数据库中),但它会被Oracle标记为无效。 Oracle 将在第一次引用该对象时尝试重新编译该对象,因此如果您的表当时存在,就可以了。

但是,当依赖关系超过一层时,您可能会遇到问题 - Oracle 不会深入依赖链来重新编译所有必要的无效对象,并且通过重新编译丢弃包的状态可能会导致问题如果之前的状态正在被另一个包使用。

【讨论】:

  • @dpbradley & @redcayuga:这似乎是一个限制性的先决条件。我要说的是 SQL Server 允许在不存在表的情况下编译存储过程,但我不太确定情况是否如此。有没有办法以编程方式要求重新编译包?
  • @tundal45 - 您可以通过标准编译语句的“EXECUTE IMMEDIATE...”以编程方式重新编译,但显然不是来自包本身。由于许多原因可能超出了本问题的范围,因此在包中创建表并不是 Oracle 的常用做法。
  • @dpbradley - 所以我最想做的更多是 CREATE TABLE AS SELECT FROM" 类型的语句,我觉得这些语句更接近于 CREATE TABLE 而不是 INSERT/UPDATE 等。这个想法在存储过程中使用它主要是为了摆脱与数据库交互的VB.NET应用程序中的内联SQL。对于这种情况,您可以推荐存储过程之外的其他选择吗?再次感谢您的宝贵时间& 输入。
【解决方案3】:

所有引用的对象,包括表和视图,在编译包时必须存在。如果表被更改或删除,则包将变得无效并且必须重新编译。所以首先在 ashish 模式中创建表。

甚至对于“AUTHID CURRENT_USER”包也是如此。这是一个有趣的情况,因为当运行包中的过程时,该过程将在调用者的模式中查找表,而不是包所有者模式,因此该表可能不存在于该模式中,Oracle 将引发运行时异常。但是包的所有者可以编译它,因为表存在于它的模式中。这很有趣,但就是这样。

【讨论】:

    【解决方案4】:

    您可以将您的 sql 编码为字符串(动态),然后将其传递给执行语句。我确信这样,oracle 不会事先知道你将要运行什么。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多