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