【问题标题】:Why do I get the error message that my cursor is invalid?为什么我会收到我的光标无效的错误消息?
【发布时间】:2020-05-17 00:19:00
【问题描述】:
SET SERVEROUTPUT ON
SET VERIFY OFF
DECLARE
v_deptno empcopy.deptno%TYPE;
v_empno empcopy.empno%TYPE;
v_sal empcopy.sal%TYPE;
v_bonus NUMBER(7,2);
CURSOR emp_cursor IS
SELECT deptno, empno, sal
FROM empcopy
WHERE v_deptno < 25;
BEGIN
FETCH emp_cursor INTO v_deptno, v_empno, v_sal;
FOR r_emp in emp_cursor LOOP
IF v_sal < 3000 THEN
v_bonus := v_sal * 1.1;
ELSE
v_bonus := v_sal * 1.12;
v_deptno := v_deptno;
v_empno := v_empno;
v_sal := v_sal;
END IF;
INSERT INTO emp3
VALUES(v_empno, v_deptno, v_sal);
FETCH emp_cursor INTO v_deptno, v_empno, v_sal;
END LOOP;
CLOSE emp_cursor;

DBMS_OUTPUT.PUT_LINE(v_deptno || v_empno || v_bonus);

END;
/
SET SERVEROUTPUT OFF
SET VERIFY ON

SQL> @ emp3
DECLARE
*
ERROR at line 1:
ORA-01001: invalid cursor
ORA-06512: at line 11

我想知道为什么它说我的游标无效。我已尝试更改必须为

【问题讨论】:

  • 在人们回答了您的原始问题后,请不要使用新标题和新代码完全重新编辑您的问题。你只是让这个线程让其他人感到困惑。如果您有新问题,请提出新问题。如果您认为相关,请务必链接到原始问题。

标签: sql oracle plsql sqlplus


【解决方案1】:

你写的代码充满了错误(你已经知道了),我不知道我是否会全部抓住它们,但我会尝试:

  • 光标选择v_deptno &lt; 25的数据。但是,v_deptno 看起来像一个本地声明的变量(您声明的第一个变量),所以 - 除非有一个名为 v_deptno 的列,否则它将失败。
    • 此外,将deptno 值硬编码为“25”根本无法扩展。我建议您切换到接受部门编号作为参数的存储过程
  • 第一个可执行行是FETCH,但它应该是OPEN(如果你选择做所有事情
    • 手动:
      • 声明一个游标
      • 声明游标变量(或单独的变量,就像你做的那样)
      • 打开光标
      • 循环
      • 从游标中获取
      • 光标%未找到时退出
      • 某事
      • 结束循环
      • 关闭光标
    • 或者,更好的是,使用游标FOR 循环,Oracle 会为您完成所有 dirty 工作:
      • FOR 光标在(SELECT 语句)LOOP
      • 某事
      • 结束循环
  • 当您检查薪水时(在IF),ELSE 是......非常奇怪。 v_deptno := v_deptno;(以及您设置的其他 3 个变量)的用途是什么?
  • INSERT 声明:它缺少您要插入的列列表。这不是一个错误(如果你做错了可能会失败),但是 - 它令人困惑,特别是当涉及到许多列时)。
    • 但是,您插入的是V_SAL。好的。那你为什么要计算V_BONUS?你从来没有使用过它(DBMS_OUTPUT 除外,但它只是显示值,就数据库中的数据而言,从来没有用它做任何事情)

好的,如果可以的话,现在我的尝试。

首先,您编写的代码,已修复(我正在插入 V_BONUS 值):

SQL> declare
  2    v_bonus number;
  3  begin
  4    for cur_r in (select deptno, empno, sal
  5                  from empcopy
  6                  where deptno < 25
  7                 )
  8    loop
  9      if cur_r.sal < 3000 then
 10         v_bonus := cur_r.sal * 1.1;
 11      else
 12         v_bonus := cur_r.sal * 1.12;
 13      end if;
 14
 15      insert into emp3 (empno, deptno, sal)
 16        values (cur_r.empno, cur_r.deptno, v_bonus);
 17    end loop;
 18  end;
 19  /

PL/SQL procedure successfully completed.

SQL> select * From emp3;

     EMPNO     DEPTNO        SAL
---------- ---------- ----------
      7369         20       1100
      7566         20     3272,5
      7782         10       2695
      7788         20       3360
      7839         10       5600
      7876         20       1210
      7902         20       3360
      7934         10       1430

8 rows selected.

SQL>

但是,这可以在 SQL 层使用单个 INSERT 语句来完成,您根本不需要 PL/SQL。所以,除非你只是在练习你的 PL/SQL 技能,否则我建议你使用这个选项:

SQL> rollback;

Rollback complete.

SQL> insert into emp3 (empno, deptno, sal)
  2    select empno,
  3           deptno,
  4           case when sal < 3000 then sal * 1.1
  5                else sal * 1.12
  6           end
  7    from empcopy
  8    where deptno < 25;

8 rows created.

SQL> select * From emp3;

     EMPNO     DEPTNO        SAL
---------- ---------- ----------
      7369         20       1100
      7566         20     3272,5
      7782         10       2695
      7788         20       3360
      7839         10       5600
      7876         20       1210
      7902         20       3360
      7934         10       1430

8 rows selected.

SQL>

最后,我在开始时建议的存储过程选项;而不是 where 子句中的 &lt;,使用 =

SQL> create or replace procedure p_bonus (par_deptno in empcopy.deptno%type)
  2  is
  3    v_bonus number;
  4  begin
  5    for cur_r in (select deptno, empno, sal
  6                  from empcopy
  7                  where deptno = par_deptno    --> "=" instead of "<"
  8                 )
  9    loop
 10      if cur_r.sal < 3000 then
 11         v_bonus := cur_r.sal * 1.1;
 12      else
 13         v_bonus := cur_r.sal * 1.12;
 14      end if;
 15
 16      insert into emp3 (empno, deptno, sal)
 17        values (cur_r.empno, cur_r.deptno, v_bonus);
 18    end loop;
 19  end;
 20  /

Procedure created.

SQL> begin
  2    p_bonus (par_deptno => 25);
  3  end;
  4  /

PL/SQL procedure successfully completed.

【讨论】:

    【解决方案2】:

    EMP3 表的列数显然少于INSERT 命令为 (4) 提供的数据。

    检查EMP3表的定义,确保表和插入命令可以匹配。

    在cmets/discussion之后,这是代码更易读的形式(我加了缩进):

    SET SERVEROUTPUT ON
    SET VERIFY OFF
    DECLARE
        v_deptno empcopy.deptno%TYPE;
        v_empno empcopy.empno%TYPE;
        v_sal empcopy.sal%TYPE;
        v_bonus NUMBER(7,2);
    
        CURSOR emp_cursor IS
            SELECT deptno, empno, sal
            FROM empcopy
            WHERE v_deptno < 25;
    BEGIN
        FETCH emp_cursor INTO v_deptno, v_empno, v_sal;
    
        FOR r_emp in emp_cursor LOOP
    
            IF v_sal < 3000 THEN
                v_bonus := v_sal * 1.1;
            ELSE
                v_bonus := v_sal * 1.12;
                v_deptno := v_deptno;
                v_empno := v_empno;
                v_sal := v_sal;
            END IF;
    
            INSERT INTO emp3
                VALUES(v_empno, v_deptno, v_sal);
    
            FETCH emp_cursor INTO v_deptno, v_empno, v_sal;
        END LOOP;
    
        CLOSE emp_cursor;
    
        DBMS_OUTPUT.PUT_LINE(v_deptno || v_empno || v_bonus);
    
    END;
    /
    SET SERVEROUTPUT OFF
    SET VERIFY ON
    

    这段代码看起来要做的是:

    • 检查表EMPCOPY中的每个“员工”记录
    • 对于每条记录,检查salary 是否低于3000,如果是,则通过将salary 乘以1.1 来计算奖金
    • 如果salary不低于3000,则v_bonus1.12计算
    • 在此之后,新值应INSERTed 到表EMP3

    • 最后,打印出最后插入的记录的值。

    首先:收入低于 3000 的穷人也只能获得 110% 的奖金,而本已较富裕的员工还能获得 2% 的额外奖金? 我知道这是一种常见的做法,但我主张以一个更美好、更公平的世界为目标——至少在像这样的简单编码示例中是这样。

    现在看代码。 这段代码应该做什么,根本不需要使用游标。事实上,在这种情况下,这并不是一个好的做法。

    相反,只需使用纯 SQL 即可达到预期效果:

    INSERT INTO EMP3 
        (empno, deptno, sal, bonus)
        (SELECT 
            empno, deptno, sal
            , case 
                when sal < 3000 then sal * 1.1
                else sal * 1.12
              end as bonus 
         FROM 
            empcopy
         WHERE 
            deptno < 25);
    

    这里需要注意的是目标表EMPCOPY 需要有一列用于BONUS。缺少此列可能是最初问题的原因。

    另外值得注意的是,这种方法不会打印出任何东西。 如果这是解决方案的要求,那么这应该在 INSERT 之后完成(应该在目标表上执行一个简单的 FOR LOOP)。

    希望对你有帮助。

    【讨论】:

    • 在更正后,它在第 1 行显示错误:ORA-06511: PL/SQL: 游标已经打开 ORA-06512: 在第 7 行 ORA-06512: 在第 13 行
    • 光标在 OPEN CURSOR 命令后打开。通过使用 FOR ... IN 代码尝试再次打开它。它是 OPEN CURSOR 或 FOR IN CURSOR。或者,代码可以先 OPEN CURSOR ... END CURSOR 然后 FOR IN CURSOR.... 但实际上,使用 FOR ... IN CURSOR 可能最简单,而不必担心手动打开/关闭游标。
    • 我解决了这个问题,当然还有另一个 SQL> @ emp3 DECLARE * ERROR at line 1: ORA-01001: invalid cursor ORA-06512: at line 11
    • 您可以使用当前代码添加/更新问题吗?我认为原来的问题已经回答了顺便说一句。
    • 我应该在一开始就提到这一点,但它必须有一个 for 循环光标,并且它必须只包含 deptno 小于 25 的员工
    猜你喜欢
    • 2015-01-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多