【问题标题】:When creating a stored procedure in oracle, it appears to execute, but it does nothing. The stored procedure never gets saved. Why?在 oracle 中创建存储过程时,它似乎正在执行,但它什么也不做。存储过程永远不会被保存。为什么?
【发布时间】:2016-01-10 02:55:11
【问题描述】:

我正在尝试使用TOAD 在 Oracle SQL 中创建一个存储过程,但它什么也没做。尝试创建它时没有错误,没有消息,没有任何内容。它似乎已经通过了,但事实并非如此。

查询如下所示:

CREATE OR REPLACE PROCEDURE PottyUseRange (formatty varchar2, start varchar2, end varchar2)
AS
    BEGIN
        SELECT TO_CHAR(TIME_RANGE, formatty) as CURRENT_DATE,
            SUM(CASE WHEN PORTA_POTTY = 'LM' THEN 1 ELSE 0 END) as LM_SEARCH,
            SUM(CASE WHEN PORTA_POTTY = 'AO' THEN 1 ELSE 0 END) as AO_SEARCH,
            SUM(CASE WHEN PORTA_POTTY = 'RO' THEN 1 ELSE 0 END) as RO_SEARCH,
            SUM(CASE WHEN PORTA_POTTY = 'FL' THEN 1 ELSE 0 END) as FL_SEARCH,
            SUM(CASE WHEN PORTA_POTTY IN ('LM', 'AO', 'RO', 'FL') THEN 1 ELSE 0 END) as TOTAL           
        FROM CORE.DATE_TEST
            WHERE to_char(TIME_RANGE, formatty) >= to_char(start, formatty)
            AND to_char(TIME_RANGE, formatty) <= to_char(end, formatty)
        GROUP BY  TO_CHAR(TIME_RANGE, formatty)
        ORDER BY TO_CHAR(TIME_RANGE, formatty)  ASC;

        EXCEPTION WHEN OTHERS THEN
            raise_application_error(-20001,'Proc failed - '||SQLCODE||' -ERROR- '||SQLERRM);
    END;

当我尝试调用它时(我知道这是一个空查询,但它会让我知道它是否存在):

BEGIN
    POTTYUSERANGE();
END;

我收到此错误:PLS-00201: identifier 'POTTYUSERANGE' must be declared

当我使用CALL POTTYUSERANGE(); 调用它时,我收到此错误:ORA-06576: not a valid function or procedure name

如何正确地为此查询创建存储过程?

更新

我需要将其插入BULK COLLECTION 并将其返回给用户。它必须受到限制,以免造成过多的内存消耗。这些是相当大的结果集,它们看起来像这样:

+--------------+----+----+----+----+-------+
| CURRENT_DATE | LM | AO | RO | FL | TOTAL |
+--------------+----+----+----+----+-------+
|  1/2/2012    | 01 | 02 | 03 | 04 |  10   |
+--------------+----+----+----+----+-------+
|  1/4/2013    | 02 | 03 | 04 | 05 |  14   |
+--------------+----+----+----+----+-------+

我需要视图吗?存储过程?我需要什么?

【问题讨论】:

  • Oracle 存储过程不只是运行 SQL。你想对结果做什么?你真的想要一个视图或用户定义的函数吗?
  • SQL*Plus中,编译程序后,这样做SHOW ERRORS
  • 另见WHEN OTHERS
  • 另外,在 Toad 中,您是如何尝试编译代码的?我在某些 Toad 版本中遇到问题,使用 F9 / Execute 语句无法运行 create or replace ... 代码,但 F5 / Execute as script 工作正常。如果有疑问,请突出显示该过程并将其作为脚本运行。
  • I need to insert this into a BULK COLLECTION .... 我强烈建议您返回一个引用光标。这将返回一个指向游标的指针,并允许调用代码在需要时将数据拉过,就像调用代码直接运行 SQL 一样。 IMO,比将所有内容收集到一个集合中然后将其一大堆传递要好得多!

标签: oracle plsql toad


【解决方案1】:

您已将过程声明为:

PottyUseRange (formatty varchar2, start varchar2, end varchar2)

但是你没有参数地调用它:

BEGIN
    POTTYUSERANGE();
END;

没有与您拨打的电话相匹配的程序。您需要传递适当数量的参数,这里可以是文字值,因为它们都是 IN 参数,例如:

BEGIN
    POTTYUSERANGE('X', 'Y', 'Z');
END;

当然,虽然具有更有意义的值。你也可以传递局部变量而不是常量字面量。

但是你说你在一个调用中得到PLS-00201: identifier 'POTTYUSERANGE' must be declared,在另一个调用中得到ORA-06576: not a valid function or procedure name,这意味着你要么根本没有真正创建它(你输入了我的代码但没有执行它),或者您正在使用两个单独的模式。您没有在 create 调用中显示架构前缀,因此出于隐私原因,您可能会被删除,或者您正在创建和调用单独的会话。如果您当前的用户不拥有该过程并且没有同义词,则需要在其前面加上所有者 - 从表所有者猜测:

BEGIN
    CORE.POTTYUSERANGE('X', 'Y', 'Z');
END;

正如拉利特所指出的,无论如何你都会有编译错误,所以调用会给出PLS-00905: object SCHEMA.POTTYUSERANGE is invalid。您可以使用show errors 或通过查询user_errors 视图(或all_errors,如果您在另一个模式中创建对象,这里似乎就是这种情况)来查看错误,这会告诉您:

PLS-00103: Encountered the symbol "START" when expecting one of the following:

         <an identifier> <a double-quoted delimited-identifier>
         current delete exists prior

开始和结束是保留字。您可以使用更合适的名称(开头是什么?)或像p_这样的通用前缀:

CREATE OR REPLACE PROCEDURE PottyUseRange (p_formatty varchar2, p_start varchar2, p_end varchar2)
AS
    BEGIN
        SELECT TO_CHAR(TIME_RANGE, formatty) as CURRENT_DATE,
            SUM(CASE WHEN PORTA_POTTY = 'LM' THEN 1 ELSE 0 END) as LM_SEARCH,
            SUM(CASE WHEN PORTA_POTTY = 'AO' THEN 1 ELSE 0 END) as AO_SEARCH,
            SUM(CASE WHEN PORTA_POTTY = 'RO' THEN 1 ELSE 0 END) as RO_SEARCH,
            SUM(CASE WHEN PORTA_POTTY = 'FL' THEN 1 ELSE 0 END) as FL_SEARCH,
            SUM(CASE WHEN PORTA_POTTY IN ('LM', 'AO', 'RO', 'FL') THEN 1 ELSE 0 END) as TOTAL           
        FROM CORE.DATE_TEST
            WHERE to_char(TIME_RANGE, p_formatty) >= to_char(p_start, formatty)
            AND to_char(TIME_RANGE, p_formatty) <= to_char(p_end, formatty)
        GROUP BY  TO_CHAR(TIME_RANGE, p_formatty)
        ORDER BY TO_CHAR(TIME_RANGE, p_formatty)  ASC;

        EXCEPTION WHEN OTHERS THEN
            raise_application_error(-20001,'Proc failed - '||SQLCODE||' -ERROR- '||SQLERRM);
    END;
/

但是当你在 PL/SQL 中工作时,你还需要select into 一些东西,例如如果您要在本地对它们做一些事情,请声明像 l_timerange 这样的局部变量。但是您似乎期待多个值,因此您需要bulk select into a collection。目前尚不清楚这应该达到什么目的。如果您想将这些值传递回调用者,使用 ref 游标返回结果集可能更简单;但是,如果您真的需要一个过程/函数,或者只是一个普通的 SQL 查询,或者可能是一个视图,那就不太清楚了......

作为另一个问题,您将日期作为字符串进行比较,并按这些字符串进行分组/排序:

to_char(TIME_RANGE, formatty) >= to_char(start, formatty)

比较仅适用于某些格式,并且当您传递可变格式时,它会提出问题;即使它有效,也可能效率不高。订购也仅适用于某些格式 - 如果订购有意义的话(再次,取决于您对结果所做的事情!)。使用传入的格式将传递的开始/结束字符串转换为日期,并比较它们:

TIME_RANGE >= to_date(p_start, formatty)

...或者如果可能的话,将日期而不是字符串传递给过程。

捕获这样的异常也很危险。您假设调用该过程的人将启用服务器输出,并对错误进行处理。除非你能明智地处理一个异常,否则你不应该捕获它,当然也不应该像这样挤压它。

【讨论】:

  • 你不能不带参数调用它,因为你试图调用一个不同的过程(因为规范不同)。
  • 我发现了另外两个严重的问题。 1.SELECT INTO 2.WHEN OTHERS 没有RAISE。异常块本身就是一个错误。
  • @SecuriTree - 您是在自己的架构中创建它吗?从 create 命令看起来像,但可能不是。您可能需要查看 all_errors 才能找到它。您选择的内容取决于您想对结果做什么;如果调用者需要返回数据,那么它所期望的或可以处理的也很重要。目前尚不清楚您为什么要使用存储过程。
  • @SecuriTree 我认为您来自非数据库背景,很可能是前端开发人员。我和其他人在这里提出的所有建议都是为了帮助你:-)。您可以尝试以下步骤: 1. SQL*Plus 应该在您的数据库安装中可用,因此请打开SQL*Plus,并编译您的程序,然后执行SHO ERR。 2.如果还是无法打开SQL*Plus,请google如何在TOAD for Oracle中使用show errors/SQL*Plus窗口。并重复这些步骤。万事如意!
  • INTO 子句错误不过是我已经说过看这个docs.oracle.com/cd/E11882_01/appdev.112/e25519/… 基本上,SQLPL/SQL 引擎是不同的,所以你不能直接在 PL/ 中执行 SQL 语句SQL。
【解决方案2】:

如果我是你,我会这样编写上述程序:

create or replace procedure pottyuserange (p_date_format in varchar2,
                                           p_start_date in varchar2,
                                           p_end_date in varchar2,
                                           p_ref_cursor out sys_refcursor)
as
begin
  open p_ref_cursor for 
    select   to_char(time_range, p_date_format) as current_date,
             lm_search,
             ao_search,
             ro_search,
             fl_search,
             total
    from     (select   trunc(time_range) time_range,
                       sum(case when porta_potty = 'LM' then 1 else 0 end) as lm_search,
                       sum(case when porta_potty = 'AO' then 1 else 0 end) as ao_search,
                       sum(case when porta_potty = 'RO' then 1 else 0 end) as ro_search,
                       sum(case when porta_potty = 'FL' then 1 else 0 end) as fl_search,
                       sum(case when porta_potty in ('LM', 'AO', 'RO', 'FL') then 1 else 0 end) as total           
              from     core.date_test
              where    trunc(time_range) >= to_date(p_start_date, p_date_format)
              and      trunc(time_range) <= to_date(p_end_date, p_date_format)
              group by trunc(time_range))
    order by time_range asc;
end pottyuserange;
/

注意:

  1. 增加out参数返回光标
  2. open p_ref_cursor for 行的添加,这是创建指向光标的指针的原因
  3. 更改谓词以将日期比较作为 DATE 而不是字符串
  4. 在关闭END 行后添加过程名称
  5. 参数名称更清晰。我建议将过程的名称也更改为更清晰的名称 - 这样,您的代码将变得更加自我记录,并且将来更易于维护。
  6. 我将查询的基础移动到子查询并将外部查询更改为直接按 time_range 字段排序的方式 - 因为这仍然是 DATE 格式,它将按预期对结果进行排序。感谢 Alex Poole 指出订购问题。

至于您在 Toad 中运行此程序时遇到的问题,某些版本的 Toad 存在一个错误(根据我的经验),通过 Execute as 语句/F9 按钮运行代码无法执行任何操作。如果是这种情况,请尝试将其作为脚本运行 (F5)。


要在 Toad(作为脚本)或 SQL*Plus 中测试上述过程,请运行以下命令:

variable rc refcursor;

begin
  PottyUseRange('YYYY-MM-DD', '1/1/2008', '10/12/2015', :rc);
end;
/

print rc;

(这将创建一个 SQLPlus 变量“rc”,您将其作为绑定变量传递给过程。然后您可以使用 SQLPlus 打印函数循环并显示结果。)


FWIW,这是我在 SQL*Plus 中运行 show errors 时看到的:

SQL*Plus: Release 11.2.0.3.0 Production on Mon Oct 12 17:34:32 2015

Copyright (c) 1982, 2011, Oracle.  All rights reserved.

Enter password:

Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.2.0 - 64bit Production
With the Partitioning, Automatic Storage Management, OLAP, Data Mining
and Real Application Testing options

SQL> create or replace procedure test
  2  as
  3  begin
  4    null;
  5  end test;
  6  /

Procedure created.

SQL> show errors;
No errors.
SQL>

【讨论】:

  • 您已经留下了对formatty 的几个引用,但我认为 OP 得到了要点 *8-) 但可能值得一提的是,如果格式为不是有用的东西(例如 DD/MM/YYYY)?如果您要更改名称,不要太热衷于 current_date 别名 - 它可以工作,但可能会有点混乱。
  • @SecuriTree - 在 SQL*Plus 中,您需要 a slash on a line on its own 来执行缓冲区。 IDE 可能会也可能不会期待它,但包含它通常更安全。而且您的调用必须包含 Boneist 添加的第四个参数,该参数必须由调用者声明(因为它是一个 OUT 参数)。 (你也可以让它成为一个返回引用光标的函数)
  • @Boneist 你确定这个where trunc(time_range) &gt;= to_char(p_start_date, p_date_format) 它正在比较 L.H.S. 上的日期到 R.H.S. 上的一个字符串我想你错过了,否则应该是TO_DATE
  • @AlexPoole 我请求您取消删除您的答案,因为它有几个要点。
  • 我同意。亚历克斯,请取消删除您的答案。我刚要问自己。你在帖子中有很多非常好的建议,它会帮助其他人。
【解决方案3】:

您应该在执行之前成功编译该过程。 要查看编​​译错误,您可以使用SHOW ERRORS。从那里你可以得到你的选择语句中的错误。

【讨论】:

    【解决方案4】:

    在编译时执行以下查询以了解错误

    select * from SYS.USER_ERRORS where lower(NAME) = 'pottyuserange ' and type = 'PROCEDURE'
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-05-25
      • 2014-05-21
      • 2011-06-18
      • 1970-01-01
      • 2023-04-01
      • 1970-01-01
      • 2019-03-05
      • 1970-01-01
      相关资源
      最近更新 更多