如何检查动态游标是不是会检索不到记录?

Posted

技术标签:

【中文标题】如何检查动态游标是不是会检索不到记录?【英文标题】:How to check whether a dynamic cursor will retrieve no records?如何检查动态游标是否会检索不到记录? 【发布时间】:2018-02-25 09:33:05 【问题描述】:

我有一个程序,其结构如下:

PROCEDURE broker(prm_qgent in varchar2, prm_cursor out sys_refcursor) 
IS
  mmy_query varchar(200);
BEGIN
  OPEN prm_cursor FOR SELECT * FROM DUAL;
  mmy_query :='SELECT *some dynamic query* where 1=1';

  if prm_agent is not null then
    mmy_query := mmy_query ||'AND agent_code = ''' ||prm_agent || '''';
  end if;

  OPEN prm_cursor FOR mmy_query;
END broker;

mmy_query 是搜索条件。因此,如果代理不在表中,它应该检索零记录。

mmy_query is动态查询检索记录。如果动态查询检索记录,它只会检索 1 条记录。所以,我想检查 mmy_query 是否没有检索到记录。

试过prm_cursor%ROWTYPE,在这两种情况下都显示零记录。 试过SQL%ROWTYPE,总是显示1条记录。

【问题讨论】:

能否格式化您的代码?这对我们来说会更容易阅读。 ***.com/help/formatting 【参考方案1】:

您需要打开游标,从中获取第一条记录并检查FOUND or NOT_FOUND 属性。 注意:如果获取了第一行,那么除了关闭该游标并再次打开它(并执行再次进行相同的查询)。


根据文档(来自上面的链接),每个命名游标有 4 个属性:

%ISOPEN

named_cursor%ISOPEN 如果游标打开,则值为 TRUE,并且 如果未打开,则为 FALSE。

%FOUND

named_cursor%FOUND 具有以下值之一:

如果游标没有打开,INVALID_CURSOR 如果游标已打开但未尝试获取,则为 NULL。 如果最近的提取返回一行,则为 TRUE。 如果最近一次提取没有返回一行,则为 FALSE。

%NOTFOUND

named_cursor%NOTFOUND 具有以下值之一:

如果游标未打开,则为 INVALID_CURSOR。 如果游标已打开但未尝试获取,则为 NULL。 如果最近一次提取返回一行,则为 FALSE。 如果最近一次提取没有返回一行,则为 TRUE。

%ROWCOUNT

named_cursor%ROWCOUNT 具有以下值之一:

如果游标未打开,则为 INVALID_CURSOR。 如果游标处于打开状态,则表示到目前为止提取的行数。

如您所见,如果不打开游标并从中获取,则无法检查查询是否返回了任何行或某些行。简单示例: 返回一些行的查询:

DECLARE
   my_cursor SYS_REFCURSOR;
   y VARCHAR(10);
BEGIN
  OPEN my_cursor FOR 'select * FROM dual WHERE 1=1';
  FETCH my_cursor INTO y;
  IF my_cursor%found THEN
     DBMS_OUTPUT.PUT_LINE('FOUND');
  ELSE
     DBMS_OUTPUT.PUT_LINE('NOT FOUND');
  END IF;
  CLOSE my_cursor;
END;
/

结果:找到


返回空结果集的查询:

DECLARE
   my_cursor SYS_REFCURSOR;
   y VARCHAR(10);
BEGIN
  OPEN my_cursor FOR 'select * FROM dual WHERE 1=0';
  FETCH my_cursor INTO y;
  IF my_cursor%found THEN
     DBMS_OUTPUT.PUT_LINE('FOUND');
  ELSE
     DBMS_OUTPUT.PUT_LINE('NOT FOUND');
  END IF;
  CLOSE my_cursor;
END;
/

结果:未找到

【讨论】:

我只想要该动态查询检索到的计数,并且由于它是一个大查询,我无法重新执行该查询。如果计数为零,我想传递一条消息。 您是否想要数数并将消息传递给呼叫者?或者您想将此消息与光标一起传递给调用者?如果是后者,那么没有办法将第一条记录返回给游标,游标是只进的,为什么调用者不能打开游标并检查游标是否为空?你想做什么就打破Single Responsibility Principle,程序应该只做一件事,只执行查询,不要检查结果和生成消息。【参考方案2】:

在调用者尝试从游标中获取之前,是否会找到行是未知的。在光标打开的时候,这还没有发生。仅打开并返回游标的过程无法知道在过程完成后调用者尝试从中获取时会发生什么。

也许理论上可以从v$sql_plan 解析执行计划来获得估计的基数,如果你能找到它的sql_id(也许从查询v$sql),但这并不简单而且充其量是结果只是猜测。

您真正能做的就是执行两次查询(或执行它的简化版本,如果可能的话),请记住,如果其他会话正在修改数据,结果可能会在两次执行之间发生变化。

【讨论】:

我只想要该动态查询检索到的计数,由于它是一个大查询,我无法重新执行该查询。如果计数为零,我想传递一条消息。 好吧,正如我刚刚解释的那样,在 Oracle 实施预见未来的增强功能之前,您很不走运。

以上是关于如何检查动态游标是不是会检索不到记录?的主要内容,如果未能解决你的问题,请参考以下文章

如何检查 ref 游标是不是从 pl/sql 过程返回数据

有没有办法检查游标是不是返回没有记录?

如何获取 PostgreSQL 游标的行数?

使用游标记录作为数组

简单记录一下日常会用到的游标

SQL Server:如何根据最近的日期时间检索所有记录