ORA-01002:使用sys_refcursor时在oracle中不按顺序提取。
我期待光标中的员工列表。在我的实际场景中,我需要检查员工人数,如果人数大于零,则应使用相同的select语句。我没有尝试多次执行相同的select语句,而是尝试了以下代码段。但是我在输出游标中遇到Fetch out of sequence错误。
我的桌子:
create or replace procedure sp_temp_1 (
var_job in VARCHAR2,
cur_custid out sys_refcursor
)
AS
/* Declare Ref Cursor */
--cur_custid SYS_REFCURSOR;
/* Declare Type using Fields from the employees table. */
TYPE t_custrec IS RECORD (
firstname temp_emp.firstname%TYPE,
lastname temp_emp.lastname%TYPE
);
/* Declare Record based off of Type */
custrec t_custrec;
BEGIN
OPEN cur_custid FOR
SELECT DISTINCT firstname
,lastname
FROM temp_emp
WHERE JOB = var_job; /* Data Analyst does not exist forcing Zero Rows returned */
LOOP
FETCH cur_custid
INTO custrec;
EXIT WHEN cur_custid%notfound;
---looping happens
END LOOP;
dbms_output.put_line('Num Rows: ' || cur_custid%rowcount);
IF cur_custid%rowcount = 0
THEN
dbms_output.put_line('Zero Rows Condition Met. Opening Cursor.');
OPEN cur_custid FOR
SELECT '0' AS empid FROM dual;
END IF;
END;
我希望有员工列表,但是sys_refcursor
在Oracle中提供了“提取顺序错误”的信息:
答案 0 :(得分:1)
我不确定为什么SQL Developer会给出该错误。尝试显示绑定的光标似乎很不高兴。不过,错误是来自该输出窗口的,而不是您的代码。
如果您手动运行与SQL工作表中的脚本完全相同的测试块,并使用客户端变量将out游标绑定到该测试块,则不会出现错误:
var cur_custid refcursor;
DECLARE
VAR_JOB VARCHAR2(200);
CUR_CUSTID sys_refcursor;
BEGIN
VAR_JOB := 'teach';
SP_TEMP_1(
VAR_JOB => VAR_JOB,
CUR_CUSTID => CUR_CUSTID
);
/* Legacy output:
DBMS_OUTPUT.PUT_LINE('CUR_CUSTID = ' || CUR_CUSTID);
*/
:CUR_CUSTID := CUR_CUSTID; --<-- Cursor
--rollback;
END;
/
Num Rows: 3
PL/SQL procedure successfully completed.
print cur_custid
但是您也没有任何输出。 print
显示光标结果,但为空。
问题确实是您在过程中消耗了游标。之后:
LOOP
FETCH cur_custid
INTO custrec;
EXIT WHEN cur_custid%notfound;
---looping happens
END LOOP;
您显然遇到过cur_custid%notfound
,这意味着光标中没有剩余的行。没有自动或手动的倒带设备;因此,当调用者获得对该out变量的控制权时,光标仍然为空。或者,如果您更喜欢这样考虑,则光标指针仍指向数据的末尾-没有剩余的行可以使用。 (同样,为什么此时的过程执行向导错误尚不清楚。)
您的前提:
我需要检查员工人数,如果人数大于零,则应使用相同的select语句。而不是多次尝试执行相同的select语句
...有缺陷。您可以做您正在做的事情,但是当您使用完游标后,必须重新打开它,它仍然会重复以下语句:
IF cur_custid%rowcount = 0
THEN
dbms_output.put_line('Zero Rows Condition Met. Opening Cursor.');
OPEN cur_custid FOR
SELECT '0' AS empid FROM dual;
ELSE
dbms_output.put_line('Non-zero Rows Condition Met. Re-opening Cursor.');
OPEN cur_custid FOR
SELECT DISTINCT firstname
,lastname
FROM temp_emp
WHERE JOB = var_job; /* Data Analyst does not exist forcing Zero Rows returned */
END IF;
在这种情况下,您实际上只需要在循环中执行一次读取,并立即退出即可;甚至根本不循环,只需执行一个简单的提取即可。您只关心零/非零,而不关心实际的非零计数。
db<>fiddle,其中包含原始代码,带有第二个光标打开的新版本以及带有单个访存的第二个新版本。并稍加修改后的调用块即可通过dbms_output
输出结果。
仅进行初始计数并确定打开哪个游标可能会更简单。
奇怪的是,两个可能的光标的列数不同。大概是什么叫它应该在游标中循环,如果它在ID列中看到零,就知道没有数据?仅使那个记录它是否处理任何行会更清洁。然后,您的过程不需要任何逻辑,只需打开一个有意义的游标-如果您真的需要一个过程来执行此操作。 (您的实际情况可能比您的示例所暗示的要完成更多的工作;在这种情况下,计数方法可能仍然更合适。)
答案 1 :(得分:0)
当我使用您的代码进行较小的修改时,没有任何错误。也许我的工作代码可以为您提供帮助?让我知道您是否有任何疑问,或者我可以进一步协助您。
摘要
员工表
代码
Dim Conn As ADODB.Connection
Sub Connect()
Dim connstring As String
Set Conn = New ADODB.Connection
connstring = "connection string"
Conn.Open connstring
End Sub
Public Function Quer(Param1 As String, Param2 As String, Param3 As String)
Dim querystring1 As String
Dim rs1 As ADODB.Recordset
querystring1 = "Select " & Param1 & " FROM table WHERE Param2 = " & Param2 & " AND Param3 = " & Param3
Set rs1 = New ADODB.Recordset
rs1.Open querystring1, Conn
Record = rs1.GetRows
Quer = Record(0, 0)
End Function