何らかの理由で、過去の人々はsequence.NEXTVALを使用せずにデータを挿入しました。したがって、テーブルにデータを入力するためにsequence.NEXTVALを使用すると、その番号はすでにテーブルで使用されているため、PK違反が発生します。
次の値を更新して使用可能にするにはどうすればよいですか?現時点では、成功するまで何度も繰り返し挿入しています(INSERT INTO tbl (pk) VALUES (sequence.NEXTVAL)
)。それによりnextvalが同期されます。
これらの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;
一時的にキャッシュサイズを増やして、ダミーの選択を1回行ってから、キャッシュサイズを1にリセットできます。たとえば、
ALTER SEQUENCE mysequence INCREMENT BY 100;
select mysequence.nextval from dual;
ALTER SEQUENCE mysequence INCREMENT BY 1;
テーブルが安定状態にあり、新しい挿入が行われない期間があると期待できる場合は、これを実行する必要があります(テストなし)。
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も使用しないため、暗黙的なコミットは実行されません。もちろん、列を埋めるためにシーケンスを使用しないことを主張する人々を追い詰めて平手打ちする必要があります...
私の場合、_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で新しく追加されました。
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ソリューションよりも低速です。