Oracle에서 sequence.nextval이 어떻게 null 일 수 있습니까?


11

다음과 같이 정의 된 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 릴리스 11.2.0.1.0-64 비트 프로덕션
  • 절차는 Microsoft.Practices.EnterpriseLibrary-Data.Oracle.OracleDatabase.ExecuteReader (DbCommand 명령)를 통해 실행됩니다.
  • 응용 프로그램은 명시 적 트랜잭션에서 호출을 래핑하지 않습니다.
  • 삽입물이 간헐적으로 실패합니다-1 % 미만

어떤 상황에서 x_seq.nextval널 이 될 수 있습니까?


선택과 삽입 사이에 얼마나 많은 코드가 있습니까? 해당 코드에 BEGIN..END 블록이나 EXCEPTION 문이 있습니까? 해당 코드에서 v_id가 전혀 참조되지 않습니까? 조금 이상해 보인다. 명령문 바로 뒤에 "IF v_id IS NULL THEN .... END IF"블록을 넣고 시퀀스가 ​​실제로 v_id에 null을 할당하는 경우 디버깅 출력을 남겨 둘 수 있습니까? 잡히지 않은 일이있을 수 있으므로 BEGIN..EXCEPTION 블록에서 시퀀스 선택을 래핑하십시오. 마지막으로 삽입하려는 테이블에 트리거가 있습니까?
Philᵀᴹ

@Phil-선택은 삽입 직전입니다. 절차 BEGIN / END 이외의 BEGIN, END 또는 EXCEPTION이 없습니다. v_id시퀀스 선택, 삽입 및 최종 커서에서만 참조됩니다. 다음 단계는 디버깅 코드를 추가하는 것이 었습니다. 결과는 프로덕션에서만 발생하고 매우 드물기 때문에 결과를 기다려야 할 수도 있습니다. 감사 테이블에 삽입하는 트리거가 있습니다. 나는 금연 총으로 그것을 빗질했습니다. 트리거가없는 다른 테이블에서도이 문제가 발생합니다. 한번 봐 주셔서 감사합니다.
Corbin 3 월

5
내가 지금 정말로 생각할 수있는 유일한 것은 : new.the_id가 어떻게 든 테이블 X에있는 트리거에서 NULL이 될 것입니다.
Phil

@ 필 : 이것은 분명히 문제의 원인입니다. 답을 만들어야합니다.
René Nyffenegger

@ RenéNyffenegger-이 문제는 트리거없이 테이블에 삽입되는 procs에서도 발생합니다. 기회 균등 버그 인 것 같습니다.
코빈 3 월

답변:


4

이것이 코드의 아티팩트 또는 사용중인 .net 드라이버가 될 것이라고 확신합니다. 순수한 SQL-PL / SQL을 사용하여 빠른 데모를 작성했으며 절대 손실 된 시퀀스 값을 얻지 못했습니다. 우연히도 사용중인 참조 커서는 아마도 불필요하며 코드의 성능과 가독성에 영향을 줄 수 있습니다. 내 데모에는 랩탑에서 26 초, 참조 커서 버전에서 36 초 동안 10 % 이상 빠르게 수행하는 insert_record2 프로 시저가 포함되어 있습니다. 적어도 이해하기 쉽다고 생각합니다. 감사 트리거가 완료된 테스트 데이터베이스에 대해 수정 된 버전을 실행할 수 있습니다.

/* 
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;

1
그런데 the_id 열에 대한 트리거를 사용하는 전통적인 접근 방식과 다음과 같은 절차를 사용하는 버전도 PROCEDURE Insert_Record3 (p_name IN dbse13142.name % type, p_userid IN dbse13142.userid % type, p_theid OUT dbse13142를 더 빠르게 작성하거나 대체했습니다. .the_id % type) 삽입 된 dbse13142 (이름, 사용자 ID) 값 (p_name, p_userid) _id를 p_theid로 리턴합니다. 종료; /
Niall Litchfield

앱 코드 또는 드라이버에 문제가있을 수 있음에 동의했습니다. 부작용으로 null nextval을 일으킬 수있는 것이 무엇인지 궁금합니다. 수수께끼. 성능 팁에 감사드립니다. 팀에 제안하는 것이 좋습니다.
Corbin 3

1
Corbin, 내 말은 (그리고 Kevin)은 코드와 오라클 사이에 이상한 일이 있다는 것입니다. SQL에서 순수하게 테스트를 실행하면 효과가 없습니다. 그러나 감사 트리거에 대한 Phil의 의견을 참조하십시오 (이 기능은 비활성화 할 수 있습니다).
Niall Litchfield

나는 그 요점을 이해한다. 트리거가 있거나없는 테이블에 프로세스를 삽입 할 때 문제가 발생하므로 트리거가 필요하지 않습니다. 트리거가 존재하면 단순히 감사 테이블에 삽입됩니다. 나는 :new.the_id손길이 닿지 않은 것을 확인했다 . 내 질문이 오래 걸린다는 것을 이해합니다. 내 구글 푸에 저항력이 있으며 여러 사람들이 여기 머리를 긁적입니다. 나는 단지 누군가가 충분한 안구가 제공된 증상 (및 치료)을 인식 할 수 있다고 생각했습니다. 한번 봐 주셔서 감사합니다.
코빈 3

2

테스트 사례를 시도하십시오. 더미 테이블을 만들고 데이터베이스의 시퀀스를 사용하여 100,000 개의 레코드를 삽입하십시오. 나는 당신에게 아무런 문제가 없을 것이라고 내기하고 있습니다. 다음으로 응용 프로그램에서 같은 것을 삽입하십시오.

Oracle 클라이언트 불일치와 같은 다른 문제로 인해 발생할 수 있습니까?

문제를 해결하지만 문제가 아닌 다른 해결책은 테이블에 트리거를 추가하는 것입니다.
Dallas.X의 테이블에 삽입하기 전에 IF : the_id가 null THEN SELECT x_seq.nextval INTO : the_id FROM dual; END IF;


로컬로 문제를 재현 할 수 없습니다. 프로덕션에서만 발생하며 드물게 발생합니다. 내 직감은 당신이 Oracle 클라이언트에 대해 옳다는 것입니다. 클라이언트가 업데이트 되지 않은 릴리스 중 몇 주 전에 문제가 발생했습니다 . 그러나 앱과 db 사이에 문제가 발생하지 않는 것 같습니다. 다른 소비자와의 상호 작용은 잘 작동하는 것 같습니다. null 검사는 나쁜 생각이 아니지만 이상적으로는 문제의 근본 원인을 해결하고 싶습니다. 그래도 누가 알아? 해결 방법은 깨진 것보다 낫습니다.
Corbin 3

0

아직 의견을 말할 권한이 없으므로 이것을 답변으로 작성하십시오 .Oracle 버전> = 11.1을 사용하고 있기 때문에 SQL 대신 PL / 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 인스턴스가 없습니다.


확실히 차이가 없습니다. 나는 select into...9i와 10g만큼 11을 사용 합니다. 11+의 유일한 이점은 지적한대로 명시 적으로 참조 할 수 있다는 것입니다.
Ben
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.