web-dev-qa-db-ja.com

Oracleシーケンスを既存の列の次の値にリセットする最良の方法は?

何らかの理由で、過去の人々はsequence.NEXTVALを使用せずにデータを挿入しました。したがって、テーブルにデータを入力するためにsequence.NEXTVALを使用すると、その番号はすでにテーブルで使用されているため、PK違反が発生します。

次の値を更新して使用可能にするにはどうすればよいですか?現時点では、成功するまで何度も繰り返し挿入しています(INSERT INTO tbl (pk) VALUES (sequence.NEXTVAL))。それによりnextvalが同期されます。

41
Cade Roux

これらの2つの手順により、シーケンスをリセットし、テーブル内のデータに基づいてシーケンスをリセットできます(このクライアントで使用されているコーディング規約の謝罪)。

CREATE OR REPLACE PROCEDURE SET_SEQ_TO(p_name IN VARCHAR2, p_val IN NUMBER)
AS
   l_num   NUMBER;
BEGIN
   EXECUTE IMMEDIATE 'select ' || p_name || '.nextval from dual' INTO l_num;

   -- Added check for 0 to avoid "ORA-04002: INCREMENT must be a non-zero integer"
   IF (p_val - l_num - 1) != 0
   THEN
      EXECUTE IMMEDIATE 'alter sequence ' || p_name || ' increment by ' || (p_val - l_num - 1) || ' minvalue 0';
   END IF;

   EXECUTE IMMEDIATE 'select ' || p_name || '.nextval from dual' INTO l_num;

   EXECUTE IMMEDIATE 'alter sequence ' || p_name || ' increment by 1 ';

   DBMS_OUTPUT.put_line('Sequence ' || p_name || ' is now at ' || p_val);
END;

CREATE OR REPLACE PROCEDURE SET_SEQ_TO_DATA(seq_name IN VARCHAR2, table_name IN VARCHAR2, col_name IN VARCHAR2)
AS
   nextnum   NUMBER;
BEGIN
   EXECUTE IMMEDIATE 'SELECT MAX(' || col_name || ') + 1 AS n FROM ' || table_name INTO nextnum;

   SET_SEQ_TO(seq_name, nextnum);
END;
16
Cade Roux

一時的にキャッシュサイズを増やして、ダミーの選択を1回行ってから、キャッシュサイズを1にリセットできます。たとえば、

ALTER SEQUENCE mysequence INCREMENT BY 100;

select mysequence.nextval from dual;

ALTER SEQUENCE mysequence INCREMENT BY 1;
78
Basanth Roy

テーブルが安定状態にあり、新しい挿入が行われない期間があると期待できる場合は、これを実行する必要があります(テストなし)。

DECLARE
  last_used  NUMBER;
  curr_seq   NUMBER;
BEGIN
  SELECT MAX(pk_val) INTO last_used FROM your_table;

  LOOP
    SELECT your_seq.NEXTVAL INTO curr_seq FROM dual;
    IF curr_seq >= last_used THEN EXIT;
    END IF;
  END LOOP;
END;

これにより、シーケンスを削除/再作成/再付与せずに、シーケンスをテーブルと同期させることができます。また、DDLも使用しないため、暗黙的なコミットは実行されません。もちろん、列を埋めるためにシーケンスを使用しないことを主張する人々を追い詰めて平手打ちする必要があります...

12
DCookie

私の場合、_PS_LOG_SEQ_というシーケンスがあり、_LAST_NUMBER = 3920_がありました。

その後、いくつかのデータをPRODからローカルマシンにインポートし、_PS_LOG_テーブルに挿入しました。実稼働データには、_20000_行より多く、最新のLOG_ID(主キー)は20070です。インポート後、このテーブルに新しい行を挿入しようとしましたが、保存時に次のような例外が発生しました。

_ORA-00001: unique constraint (LOG.PS_LOG_PK) violated
_

確かに、これは_PS_LOG_SEQ_テーブルに関連付けられたシーケンス_PS_LOG_に関係しています。 _LAST_NUMBER_は、_PS_LOG_SEQ_の次のID値を既に使用していたインポートしたデータと衝突していました。

これを解決するために、このコマンドを使用してシーケンスを最新の\ max(LOG_ID) + 1に更新しました。

_alter sequence PS_LOG_SEQ restart start with 20071;
_

このコマンドは_LAST_NUMBER_値をリセットし、テーブルに新しい行を挿入できました。もう衝突はありません。 :)

注:this _alter sequence_ コマンドはOracle 12cで新しく追加されました。

10

Oracle 10.2gの場合:

select  level, sequence.NEXTVAL
from  dual 
connect by level <= (select max(pk) from tbl);

現在のシーケンス値をテーブルのmax(pk)に設定します(つまり、NEXTVALの次の呼び出しで正しい結果が得られます)。 Toadを使用する場合、F9キーではなくF5キーを押してステートメントを実行します。F9キーは出力をページングします(したがって、通常500行後にインクリメントを停止します)。良い面:このソリューションはDMLではなくDDLのみです。 SQLのみでPL-SQLはありません。悪い面:このソリューションは、出力のmax(pk)行を印刷します。つまり、通常、ALTER SEQUENCEソリューションよりも低速です。

9
tech