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でnullになる可能性がありますか?


選択と挿入の間にどのくらいのコードがありますか?そのコードにBEGIN..ENDブロックまたはEXCEPTIONステートメントがありますか?そのコードでv_idはまったく参照されていますか?少し奇妙に思えます。「IF v_id IS NULL THEN .... END IF」ブロックをステートメントの直後に配置し、シーケンスが実際にnullをv_idに割り当てる場合、デバッグ出力をどこかに残すことはできますか?キャッチされていない何かが発生している可能性があるため、シーケンス選択をBEGIN..EXCEPTIONブロックでラップします。最後の1つ-挿入しているテーブルにトリガーがあり、それが原因である可能性がありますか?
Philᵀᴹ

@Phil-選択は挿入の直前です。proc BEGIN / END以外のBEGIN、END、またはEXCEPTIONはありません。v_idシーケンス選択、挿入、および最後のカーソルでのみ参照されます。次のステップは、デバッグコードを追加することでした。結果が本番環境でのみ発生し、まれにしか発生しないため、結果を待つ必要がある場合があります。監査テーブルに挿入するトリガーがあります。私は煙る銃なしでそれをとかしました。この問題は、トリガーのない他のテーブルでも時々発生します。ご覧いただきありがとうございます。
コービン3

5
現時点で私が本当に考えることができるのは、テーブルXにあるトリガーで:new.the_idがどういうわけかNULLになるかどうかだけです。
PhilᵀᴹFeb

@フィル:これは間違いなく問題の原因です。あなたはそれを答えにするべきです。
ルネNyffenegger

@RenéNyffenegger-この問題は、トリガーなしでテーブルに挿入するプロシージャでも発生します。機会均等のバグのようです。
コービン3

回答:


4

私はこれがあなたのコードのアーティファクトになるか、あなたが使っている.netドライバになると確信しています。純粋なSQL-PL / SQLを使用して簡単なデモを作成しましたが、シーケンス値が失われることはありません。ちなみに、使用しているrefカーソルはおそらく不要で、コードのパフォーマンスと可読性に影響を与える可能性があります-私のデモには、一貫して10%以上高速に実行するinsert_record2プロシージャが含まれています-私のラップトップでは約26秒で、refカーソルバージョンでは36秒です。少なくとも理解しやすいと思います。監査トリガーを備えたテストデータベースに対して、変更されたバージョンを実行できることは明らかです。

/* 
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)IS BEGIN INSERT INTO dbse13142(name、userid)VALUES(p_name、p_userid)は、the_idをp_theidに返します。終わり; /
Niall Litchfield

アプリのコードまたはドライバーに問題がある可能性が高いことに同意しました。副作用としてnullのnextvalを引き起こす可能性があるものに興味があります。不可解。パフォーマンスのヒントをありがとう。私がチームに提案するのは良いアドバイスです。
コービン3

1
コービン、つまり私が(そしてケビンと)言っているのは、コードとoracleの間で何か奇妙なことが起こっているということです。純粋にSQLでテストを実行すると、効果が得られません。ただし、監査トリガーに関するPhilのコメントを参照してください(無効にすることもできます)。
Niall Litchfield

なされたポイントを理解しました。トリガーがある場合とない場合のテーブルへのプロシージャの挿入に問題があるため、トリガーは必要ありません。トリガーが存在する場合は、監査テーブルに挿入するだけです。:new.the_id手つかずであることを確認しました。私の質問は大げさだと理解しています。これは私のgoogle-fuに耐性があり、何人かの人々がここで頭を掻いています。誰かが十分な眼球を与えられれば症状(と治療)を認識できるかもしれないと思ったところです。ご覧いただきありがとうございます。
コービン3

2

テストケースを試してみてください。ダミーテーブルを作成し、データベースからのシーケンスを使用して100,000レコードを挿入します。問題ないと思います。次に、アプリケーションから同じものを挿入してみます。

これは、Oracleクライアントの不一致などの他の問題が原因である可能性がありますか?

問題を修正するが問題は解決しない別の解決策は、表にトリガーを追加することです。
Dallas.Xのテーブルに挿入する前IF:the_idがNULLである場合SELECT x_seq.nextval INTO:the_id FROM dual; 終了IF;


ローカルで問題を再現することはできません。これは本番環境でのみ発生し、まれにしか発生しません。私の直感は、あなたがOracleクライアントについて正しいということです。この問題は、クライアントが更新されていないリリースの数週間前に発生しました。しかし、アプリとデータベースの間で何かがうまくいっていないように感じます。他の消費者とのやり取りはうまく機能しているようです。nullチェックは悪い考えではありませんが、理想的には、問題の根本に立ち向かい、回避するのが理想的です。誰が知っていますか?回避策は壊れているよりも優れています。
コービン3

0

私はまだコメントする特権を持っていないので、これを答えとして書きます:SQLではなくPL / SQL式でシーケンスを許可するOracleバージョン> 11.1を使用しているので、これを試してください:

   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...11で9iと10g を使用しています。11+からの唯一の利点は、あなたが指摘したようにそれを明示的に参照できることです。
ベン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.