Как может 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 быть нулевым?

11 голосов | спросил Corbin March 16 FebruaryEurope/MoscowbThu, 16 Feb 2012 00:03:36 +0400000000amThu, 16 Feb 2012 00:03:36 +040012 2012, 00:03:36

3 ответа


4

Я уверен, что это станет артефактом вашего кода или используемым драйвером .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;
ответил Niall Litchfield 16 FebruaryEurope/MoscowbThu, 16 Feb 2012 15:07:40 +0400000000pmThu, 16 Feb 2012 15:07:40 +040012 2012, 15:07:40
2

Попробуйте сделать тестовый пример. Создайте фиктивную таблицу и вставьте 100 000 записей, используя вашу последовательность из базы данных. Держу пари, у тебя не будет проблем. Затем попробуйте вставить то же самое из своего приложения.

Может ли это быть вызвано другими проблемами, такими как несоответствие клиента Oracle?

Еще одно решение, которое поможет устранить проблему, но не проблема, - добавить триггер в таблицу.
Перед вставкой на стол на Dallas.X IF: the_id имеет значение null ТОГДА         SELECT x_seq.nextval           INTO: the_id           FROM dual; END IF;

ответил kevinsky 16 FebruaryEurope/MoscowbThu, 16 Feb 2012 07:22:45 +0400000000amThu, 16 Feb 2012 07:22:45 +040012 2012, 07:22:45
0

У меня нет комментариев для комментариев, поэтому пишу это как ответ: Поскольку вы используете версию 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, чтобы попробовать это.

ответил George3 17 FebruaryEurope/MoscowbFri, 17 Feb 2012 08:05:03 +0400000000amFri, 17 Feb 2012 08:05:03 +040012 2012, 08:05:03

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132