使用游标的存储过程

Posted

技术标签:

【中文标题】使用游标的存储过程【英文标题】:Stored procedure that uses cursor 【发布时间】:2018-03-11 16:49:53 【问题描述】:

我创建了一个使用游标生成报告的过程。它旨在通过p_qoh > avg(p_qoh) 退回产品。

当我在程序之外自行运行光标时,APEX 会告诉我

PLS-00204:函数或伪列“AVG”可能在 SQL 中使用 仅声明

这怎么不在 SQL 语句中?我是 SQL 新手,只是 Comp sci 专业的必修课。

这是整个街区。如果你只运行光标,你会明白我的意思。

CREATE OR REPLACE PROCEDURE prod_rep
IS 
  CURSOR cur_qoh IS
    SELECT p_qoh, p_descript, p_code
      FROM xx_product;

  TYPE type_prod IS RECORD(
    prod_qoh    xx_product.p_qoh%TYPE,
    prod_code   xx_product.p_code%TYPE,
    prod_descr  xx_product.p_descript%TYPE);

  rec_prod type_prod;
BEGIN
  OPEN cur_qoh;
  LOOP
    FETCH cur_qoh INTO rec_prod;
    EXIT WHEN cur_qoh%NOTFOUND;

    IF rec_prod.prod_qoh > avg(rec_prod.prod_qoh) THEN
       DBMS_OUTPUT.PUT_LINE(rec_prod.prod_code||' -> '||rec_prod.prod_desc);
    END IF;
  END LOOP;
  CLOSE cur_qoh;
END;

更新:工作块

BEGIN 
 FOR cur_r IN (SELECT p_qoh, p_descript, p_code FROM xx_product
        WHERE p_qoh > (SELECT avg(p_qoh) FROM xx_product))
LOOP
  DBMS_OUTPUT.PUT_LINE(cur_r.p_code||' -> '|| cur_r.p_descript);
 END LOOP;
END;

【问题讨论】:

【参考方案1】:

嗯,是的 - 这是 (PL/)SQL,但您不能那样使用 AVG。看看:这是一个基于 Scott 架构的示例。

平均工资是

SQL> select avg(sal) from emp;

  AVG(SAL)
----------
2077,08333

为了选择高于平均水平的薪水,您可以使用子查询。让我们将此查询称为“A”查询(以供将来参考):

SQL> select ename, sal
  2  from emp
  3  where sal > (select avg(sal) from emp);

ENAME             SAL
---------- ----------
JONES            2975
BLAKE            2850
CLARK            2450
KING             5000
FORD             3000

SQL>

另外,不需要声明那么多东西——一个简单的游标 FOR 循环更容易维护:

SQL> create or replace procedure prod_rep as
  2  begin
  3    for cur_r in (select ename, sal from emp
  4                  where sal > (select avg(sal) from emp))
  5    loop
  6      dbms_output.put_line(cur_r.ename ||' '|| to_char(cur_r.sal, '9990'));
  7    end loop;
  8  end;
  9  /

Procedure created.

SQL> begin prod_rep; end;
  2  /
JONES  2975
BLAKE  2850
CLARK  2450
KING  5000
FORD  3000

PL/SQL procedure successfully completed.

SQL>

看到了吗?无需声明游标(好的,您确实在循环中使用了该 SELECT),键入和记录该类型,打开游标,担心何时退出循环,关闭游标。


但是,您的代码在 Apex 环境中没有多大意义。那里没有 DBMS_OUTPUT,使用过程创建报告(经典或交互式报告)是相当不寻常的;我从来没有这样做过。我使用了一个函数(它是一个 PL/SQL 代码),它返回一个 SQL 查询并基于它的报告。

您的问题很简单,因此 - 使用向导,创建报告并 - 作为其来源 - 使用“A”查询(我之前提到过)。这就是你应该做的。

【讨论】:

谢谢!我对其进行了更新,以包含我在您的帮助下制作的有效块,但是当我尝试将其放入过程中时,它返回“编译错误成功” CREATE 只是创建一个过程。您必须执行它才能运行它的代码并获得结果——这就是我的BEGIN prod_rep; END; / 块所做的。确保启用输出,即运行set serveroutput on(否则您将不会在屏幕上看到任何内容)。 我的错误,我错过了 AS 关键字。它现在正确编译。感谢您的帮助!【参考方案2】:

此时您进入的是 PL/SQL,而不是 SQL。 您可以在 SQL 查询中使用分析函数 AVG 来获取平均值:

CURSOR cur_qoh IS
SELECT p_qoh, p_descript, p_code, AVG(p_goh) OVER () as p_goh_avg
  FROM xx_product;

您可以将新字段添加到 type_prod:

  TYPE type_prod IS RECORD(
    prod_qoh    xx_product.p_qoh%TYPE,
    prod_code   xx_product.p_code%TYPE,
    prod_descr  xx_product.p_descript%TYPE,
    prod_avg    xx_product.p_qoh%TYPE)
   ;

然后你可以将你的平均值用于循环:

IF rec_prod.prod_qoh > rec_prod.prod_avg THEN

在 PL/SQL 循环中使用 AVG 没有意义。 PL/SQL 是过程性的,此时您无需处理整组数据:一次只管理一行。而 SQL 处理数据集。

使用分析函数,您可以获得每行列的平均值,因此您可以在 PL/SQL 循环中对其进行管理。

【讨论】:

以上是关于使用游标的存储过程的主要内容,如果未能解决你的问题,请参考以下文章

Oracle存储过程游标for循环怎么写

Oracle存储过程游标for循环怎么写

Oracle存储过程游标for循环怎么写

关于存储过程返回游标的错误

mysql中存储过程和游标调用问题

oracle存储过程返回游标,取值报错