SQL%FOUND,其中SELECT查询不返回任何行

时间:2016-01-01 22:04:37

标签: oracle plsql

我有以下功能,它会从Client表中返回下一个可用客户端ID

CREATE OR REPLACE FUNCTION getNextClientID RETURN INT AS
ctr INT;

BEGIN
SELECT MAX(NUM) INTO ctr FROM Client;

IF SQL%NOTFOUND THEN
    RETURN 1;
ELSIF SQL%FOUND THEN
    -- RETURN SQL%ROWCOUNT;
    RAISE_APPLICATION_ERROR(-20010, 'ROWS FOUND!');
    -- RETURN ctr + 1;
END IF; 

END;

但是在调用此函数时,

BEGIN 
DBMS_OUTPUT.PUT_LINE(getNextClientID());

END;

我得到以下结果:

execution

我发现有点奇怪,因为Client表不包含数据:

Client table

另外,如果我发表评论RAISE_APPLICATION_ERROR(-20010, 'ROWS FOUND!');&将SQL%ROWCOUNT的值记录到控制台,结果我得到 1

另一方面,改变时

SELECT MAX(NUM) INTO ctr FROM Client;

SELECT NUM INTO ctr FROM Client;

执行按预期进行。 这种行为背后的原因是什么?

4 个答案:

答案 0 :(得分:3)

Aggregate functions将始终返回结果:

  

除COUNT(*),GROUPING和GROUPING_ID之外的所有聚合函数   忽略空值。您可以在参数中使用NVL函数   用于将值替换为null的聚合函数。 COUNT和   REGR_COUNT永远不会返回null,但返回数字或零。对于   所有剩余的聚合函数,如果数据集不包含   行,或仅包含具有空值的行作为聚合的参数   函数,然后函数返回null。

您可以将查询更改为:

SELECT COALESCE(MAX(num), 1) INTO ctr FROM Client;

并完全删除条件。如果不使用SELECT FOR UPDATE,请注意并发问题。

答案 1 :(得分:2)

使用任何聚合函数进行查询且没有GROUP BY子句总是返回1行。如果您希望空表上出现no_data_found例外,请添加GROUP BY子句或删除max

SQL> create table t (id number, client_id number);

Table created.

SQL> select nvl(max(id), 0) from t;

NVL(MAX(ID),0)
--------------
         0

SQL> select nvl(max(id), 0) from t group by client_id;

no rows selected

通常使用像您这样的查询(使用max而不使用group by)来避免no_data_found

答案 2 :(得分:1)

像MAX这样的Agregate函数将始终返回一行。如果没有找到行,它将返回一行为空值。

顺便说一下,SELECT NUM INTO ctr FROM Client;会引发一个异常,表中有多行。

您应该检查ctr是否为空。

答案 3 :(得分:1)

其他人已经解释了为什么你的代码没有“工作”的原因,所以我不打算这样做。

您似乎是在自己创建一些描述的标识列,可能是为了支持代理键。 自行执行此操作非常危险,可能会导致应用程序出现大问题。

您不需要自己实施标识列。从Oracle 12c开始,Oracle对identity columns提供原生支持,这些支持使用sequences实现,这些版本在12c和之前的版本中可用。

序列是一个数据库对象,无论请求值的并发会话数是多少,都可以保证在调用时提供新的唯一数字。当多个会话使用时,您当前的方法极易受到冲突的影响。想象一下,2个会话同时找到表中的最大值;然后他们都为此值添加一个并尝试将此新值写回。只有一个是正确的。

请参阅How to create id with AUTO_INCREMENT on Oracle?

基本上,如果您使用序列,那么您就不需要任何此代码。

作为次要说明,您在顶部的陈述不正确:

  

我有以下函数,它从Client表

返回下一个可用的客户端ID

您的函数返回最大ID + 1.如果ID中存在间隙,即1,2,3,5,那么"缺少"号码(在这种情况下为4)将不予退还。由于各种原因(例如删除行)可能会出现间隙,并且不会以任何方式对您的数据库产生负面影响 - 不用担心它们。

相关问题