Oracle 找到SQL%,其中SELECT查询不返回任何行

Oracle 找到SQL%,其中SELECT查询不返回任何行,oracle,plsql,Oracle,Plsql,我有以下函数,它从客户机表返回下一个可用的客户机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_ERRO

我有以下函数,它从
客户机
表返回下一个可用的客户机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;
我得到以下结果:

我发现这有点奇怪,因为
客户机
表不包含任何数据:

另外,如果我注释掉
RAISE_APPLICATION_ERROR(-20010,'找到行!')
&将
SQL%ROWCOUNT
的值记录到控制台,结果得到1

另一方面,当改变

SELECT MAX(NUM) INTO ctr FROM Client;

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

将始终返回一个结果:

除COUNT(*)、GROUPING和GROUPING\u ID之外的所有聚合函数 忽略空值。您可以在参数中使用NVL函数 聚合函数以将值替换为null。计数和 REGR_COUNT从不返回null,而是返回数字或零。对于 所有剩余的聚合函数,如果数据集不包含 行,或仅包含以Null作为聚合参数的行 函数,则该函数返回null

您可以将查询更改为:

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

并删除所有条件。但要注意并发性问题,如果不使用
选择更新

,则诸如MAX之类的拒绝函数将始终返回一行。如果找不到行,它将返回一个带有空值的行

通过
从客户端选择NUM进入ctr将在表中有多行时引发异常


您应该检查
ctr
是否为空。

使用任何聚合函数而不使用
GROUP BY
子句的查询始终返回1行。如果要在空表上找到
无数据
异常,请添加
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
和不使用
groupby
)用于避免
找不到任何数据

其他人已经解释了代码不“工作”的原因,所以我不打算这样做

您似乎自己设置了一个某种描述的标识列,可能是为了支持代理密钥自己这样做是危险的,可能会在应用程序中造成大问题。

您不需要自己实现标识列。从Oracle 12c开始,Oracle就提供了对的本机支持,这些支持是使用实现的,在12c和以前的版本中都有

序列是一个数据库对象,它保证在调用时提供一个新的、唯一的编号,而不管请求值的并发会话的数量如何。当前方法在多个会话中使用时极易发生冲突。想象两个会话同时发现表中的最大值;然后,它们都向该值添加一个值,并尝试将该新值写回。只有一个是正确的

基本上,如果您使用一个序列,那么您不需要这些代码


第二,您在顶部的陈述不正确:

我有以下函数,它从客户机表返回下一个可用的客户机ID


函数返回最大ID+1。如果ID中存在间隙,即1、2、3、5,则不会返回“缺失”编号(本例中为4)。间隙可能由于多种原因(例如删除一行)而出现,并且不会对数据库产生任何负面影响-不用担心。

MAX是一个聚合函数,它总是返回一个结果:在这种情况下为NULL。哦,我明白了。在这种情况下,我如何检查它是否返回
null
?我感谢您的帮助:)请摆脱使用
NVL()的习惯。它本质上是危险的,因为它隐式地将第二个参数转换为第一个参数的数据类型。改为使用
COALESCE()
,这将返回完全相同的结果,但如果意外地隐式转换数据类型,则会引发错误
COALESCE()
也是ANSI标准,因此使您的代码更易于移植。@Ben我添加了
nvl
,只是为了显示不带
group by
的查询返回1行(否则查询返回空字符串,并且在SQL*中可能与结果中没有行混淆)。谢谢@Ben的回复+1对于你的多次会议记录,我不知道。关于标识栏和序列,我知道它们(我在问这个问题之前查看了您所附的链接),我发现正如您所说,标识栏仅在12c版本中可用。另外,这是学校的工作,所以我不能真的使用它们两个。。。关于函数行为,我意识到了潜在的差距,希望我可以在表上使用一个触发器来解决这个问题,该触发器将在每次插入或删除时更新
id
s。请不要添加一个触发器来更新@Sparta表,我提到它是为了让您知道这并不重要。如果更新表,则每次删除一行时都必须更新整个表,这将使代码效率极低。序列在12c之前可用,我已经链接到3种不同的使用方法。我现在会养成做正确事情的习惯。如果您的下一个项目是扩展此数据库,那么您将立即遇到问题。序列是简单、容易、选择,需要更少的代码和更少的错误机会。我感谢你们的关心,我会考虑它们并牢记它们。我已经从你那里学到了很多,谢谢!PS:欢迎来到摩洛哥,在那里你不能尝试任何新的或有用的东西,除非在课程中列出:请摆脱使用
NVL()
的习惯。它本质上是危险的,因为它隐式地将第二个参数转换为第一个参数的数据类型。使用
COALESCE()
inst
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