Как может sequence.nextval быть нулевым в Oracle?
У меня есть последовательность 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 Enterprise Edition Release 11.2.0.1.0 - 64bit Production
- Процедура выполняется через Microsoft.Practices.EnterpriseLibrary - Data.Oracle.OracleDatabase.ExecuteReader (команда DbCommand)
- Приложение не переносит вызов в явной транзакции.
- Вставка прерывается с перерывами - менее 1%
При каких обстоятельствах x_seq.nextval
быть нулевым?
3 ответа
Я уверен, что это станет артефактом вашего кода или используемым драйвером .net. Я выполнил быструю демонстрацию для вас, используя чистый SQL-PL /SQL и никогда не получаю потерянное значение последовательности. Кстати, используемый вами курсор ref, вероятно, не нужен и, вероятно, влияет на производительность и читаемость кода. В моей демонстрации включена процедура insert_record2, которая последовательно выполняет более 10% быстрее - примерно в 26 секунд на моем ноутбуке против 36 для версии курсора ref. По крайней мере, я думаю, это легче понять. Очевидно, вы можете запустить модифицированную версию с тестовой базой данных с триггером аудита.
/*
demo for dbse
assumes a user with create table, create sequence, create procedure pivs and quota.
*/
drop table dbse13142 purge;
create table dbse13142(
the_id number not null
, name varchar2(20)
, userid number)
;
drop sequence x_seq;
CREATE SEQUENCE X_SEQ NOCACHE NOORDER NOCYCLE ;
create or replace PROCEDURE Insert_Record
(p_name IN VARCHAR2,
p_userid IN INTEGER,
cur_out OUT sys_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 dbse13142
(the_id,
name,
userid)
VALUES
(v_id,
p_name,
p_userid);
-- Return new id
OPEN cur_out FOR
SELECT v_id the_id
FROM dual;
END;
/
create or replace PROCEDURE Insert_Record2
(p_name IN VARCHAR2,
p_userid IN INTEGER,
p_theid OUT dbse13142.the_id%type)
IS
BEGIN
-- Get id value from sequence
SELECT x_seq.nextval
INTO p_theid
FROM dual;
-- Line below is X_PKG line 40
INSERT INTO dbse13142
(the_id,
name,
userid)
VALUES
(p_theid,
p_name,
p_userid);
END;
/
set timing on
declare
c sys_refcursor;
begin
for i in 1..100000 loop
insert_record('User '||i,i,c);
close c;
end loop;
commit;
end;
/
select count(*) from dbse13142;
truncate table dbse13142;
declare
x number;
begin
for i in 1..100000 loop
insert_record2('User '||i,i,x);
end loop;
commit;
end;
/
select count(*) from dbse13142;
truncate table dbse13142;
Попробуйте сделать тестовый пример. Создайте фиктивную таблицу и вставьте 100 000 записей, используя вашу последовательность из базы данных. Держу пари, у тебя не будет проблем. Затем попробуйте вставить то же самое из своего приложения.
Может ли это быть вызвано другими проблемами, такими как несоответствие клиента Oracle?
Еще одно решение, которое поможет устранить проблему, но не проблема, - добавить триггер в таблицу.
Перед вставкой на стол на Dallas.X
IF: the_id имеет значение null
ТОГДА
SELECT x_seq.nextval
INTO: the_id
FROM dual;
END IF;
У меня нет комментариев для комментариев, поэтому пишу это как ответ: Поскольку вы используете версию Oracle> = 11.1, которая позволяет последовательности в выражениях PL /SQL вместо SQL, попробуйте следующее:
v_id := x_seq.nextval;
Вместо этого:
-- Get id value from sequence
SELECT x_seq.nextval
INTO v_id
FROM dual;
Или, хотя я слышал сомнения /ловушки при использовании «.currval», возможно, опустить отдельное назначение v_id и использовать этот код только:
-- Line below is X_PKG line 40
INSERT INTO X
(the_id,
name,
update_userid)
VALUES
(x_seq.nextval,
p_name,
p_userid);
-- Return new id
OPEN cur_out FOR
SELECT x_seq.currval the_id
FROM dual;
Жаль, что сейчас у меня нет экземпляра 11g, чтобы попробовать это.