我有一个这样定义的 Oracle 序列:
CREATE SEQUENCE "DALLAS"."X_SEQ"
MINVALUE 0
MAXVALUE 999999999999999999999999999
INCREMENT BY 1 START WITH 0 NOCACHE NOORDER NOCYCLE ;
它在存储过程中用于插入记录:
PROCEDURE Insert_Record
(p_name IN VARCHAR2,
p_userid IN INTEGER,
cur_out OUT TYPES_PKG.RefCursor)
IS
v_id NUMBER := 0;
BEGIN
-- Get id value from sequence
SELECT x_seq.nextval
INTO v_id
FROM dual;
-- Line below is X_PKG line 40
INSERT INTO X
(the_id,
name,
update_userid)
VALUES
(v_id,
p_name,
p_userid);
-- Return new id
OPEN cur_out FOR
SELECT v_id the_id
FROM dual;
END;
有时,从应用程序代码执行此过程时会返回错误。
ORA-01400: cannot insert NULL into ("DALLAS"."X"."THE_ID")
ORA-06512: at "DALLAS.X_PKG", line 40
ORA-06512: at line 1
可能相关或不相关的详细信息:
- Oracle Database 11g 企业版 11.2.0.1.0 - 64 位生产
- 该过程通过 Microsoft.Practices.EnterpriseLibrary - Data.Oracle.OracleDatabase.ExecuteReader(DbCommand 命令)执行
- 应用程序不会将调用包装在显式事务中。
- 插入间歇性失败 - 不到 1%
什么情况下可以x_seq.nextval
为空?
我很确定这最终会成为您的代码或您正在使用的 .net 驱动程序的工件。我使用纯 SQL - PL/SQL 为您完成了一个快速演示,并且永远不会丢失序列值。顺便说一句,您使用的参考光标可能是不必要的,并且可能会影响代码的性能和可读性 - 我的演示包括一个 insert_record2 过程,该过程始终快 10% 以上 - 在我的笔记本电脑上大约 26 秒,而参考光标版本为 36 秒。我至少也认为更容易理解。您显然可以针对您的测试数据库运行修改后的版本,并完成审计触发器。
尝试做一个测试用例。制作一个虚拟表并使用数据库中的序列插入 100,000 条记录。我打赌你不会有任何问题。接下来尝试从您的应用程序中插入相同的内容。
这可能是由 Oracle 客户端不匹配等其他问题引起的吗?
另一个可以解决问题但不是问题的解决方案是在表上添加触发器。
在 Dallas.X 上的表上插入之前 IF :the_id 为空 THEN SELECT x_seq.nextval INTO :the_id FROM dual; 万一;
我还没有发表评论的特权,所以写下这个作为答案:由于您使用的是 Oracle 版本 >= 11.1,它允许使用 PL/SQL 表达式而不是 SQL 中的序列,试试这个:
而不是这个:
或者,虽然我在使用“.currval”时听到了疑虑/陷阱,但也许省略了 v_id 的单独分配而只使用此代码?:
抱歉,我现在手头没有 11g 实例来尝试一下。