回答:
次に、Oracleの第一人者Tom Kyteからシーケンスを0にリセットするための適切な手順を示します。以下のリンクでも賛否両論の素晴らしい議論。
tkyte@TKYTE901.US.ORACLE.COM>
create or replace
procedure reset_seq( p_seq_name in varchar2 )
is
l_val number;
begin
execute immediate
'select ' || p_seq_name || '.nextval from dual' INTO l_val;
execute immediate
'alter sequence ' || p_seq_name || ' increment by -' || l_val ||
' minvalue 0';
execute immediate
'select ' || p_seq_name || '.nextval from dual' INTO l_val;
execute immediate
'alter sequence ' || p_seq_name || ' increment by 1 minvalue 0';
end;
/
このページから:シーケンス値をリセットするための動的SQL
もう1つの良い議論もここにあります:シーケンスをリセットする方法?
execute immediate
最大で1行を返す選択の出力をキャプチャするために使用する場合の構文です。即時実行に関するドキュメントは次のとおり
'alter sequence ' || p_seq_name || ' increment by 1 minvalue 0';
真の再起動は不可能です。(私が間違っている場合は修正してください!)
ただし、0に設定したい場合は、削除して再作成するだけです。
特定の値に設定する場合は、INCREMENTを負の値に設定して、次の値を取得できます。
つまり、シーケンスが500の場合、次のようにして100に設定できます。
ALTER SEQUENCE serial INCREMENT BY -400;
SELECT serial.NEXTVAL FROM dual;
ALTER SEQUENCE serial INCREMENT BY 1;
これは私のアプローチです:
例:
--Drop sequence
DROP SEQUENCE MY_SEQ;
-- Create sequence
create sequence MY_SEQ
minvalue 1
maxvalue 999999999999999999999
start with 1
increment by 1
cache 20;
alter sequence serial restart start with 1;
この機能は18cで公式に追加されましたが、12.1では非公式に利用可能です。
12.1でこの文書化されていない機能を使用することは間違いなく安全です。構文は公式ドキュメントに含まれていませんが、OracleパッケージDBMS_METADATA_DIFFによって生成されます。実動システムで何度か使用しました。しかし、Oracleサービスリクエストを作成し、ドキュメントのバグではないことを確認しました。この機能は本当にサポートされていません。
18cでは、この機能はSQL言語構文には表示されませんが、 『データベース管理者ガイド』に含まれています。
... RESTART START WITH 0 MINVALUE 0
私のアプローチは、ダグマンの例に対するほんの少しの拡張です。
拡張機能は...
シード値をパラメーターとして渡します。どうして?シーケンスをいくつかのテーブルで使用されている最大IDにリセットするものを呼び出すのが好きです。一連のシーケンス全体に対して複数の呼び出しを実行する別のスクリプトからこのプロシージャを呼び出し、nextvalを、一意の識別子にシーケンスの値を使用している主キー違反を引き起こさないほど高いレベルにリセットします。
また、以前のminvalueを尊重します。実際に、必要なp_valまたは既存の最小値が現在の値または計算された次の値よりも高い場合、次の値をさらに押し上げる可能性があります。
特に、指定した値にリセットするために呼び出すことができ、最後にラッパーの「すべてのシーケンスを修正する」プロシージャが表示されるまで待つだけです。
create or replace
procedure Reset_Sequence( p_seq_name in varchar2, p_val in number default 0)
is
l_current number := 0;
l_difference number := 0;
l_minvalue user_sequences.min_value%type := 0;
begin
select min_value
into l_minvalue
from user_sequences
where sequence_name = p_seq_name;
execute immediate
'select ' || p_seq_name || '.nextval from dual' INTO l_current;
if p_Val < l_minvalue then
l_difference := l_minvalue - l_current;
else
l_difference := p_Val - l_current;
end if;
if l_difference = 0 then
return;
end if;
execute immediate
'alter sequence ' || p_seq_name || ' increment by ' || l_difference ||
' minvalue ' || l_minvalue;
execute immediate
'select ' || p_seq_name || '.nextval from dual' INTO l_difference;
execute immediate
'alter sequence ' || p_seq_name || ' increment by 1 minvalue ' || l_minvalue;
end Reset_Sequence;
その手順はそれ自体で便利ですが、それを呼び出して、シーケンスの命名規則を使用してプログラムですべてを指定し、既存のテーブル/フィールドで使用されている最大値を探す別の手順を追加しましょう...
create or replace
procedure Reset_Sequence_to_Data(
p_TableName varchar2,
p_FieldName varchar2
)
is
l_MaxUsed NUMBER;
BEGIN
execute immediate
'select coalesce(max(' || p_FieldName || '),0) from '|| p_TableName into l_MaxUsed;
Reset_Sequence( p_TableName || '_' || p_Fieldname || '_SEQ', l_MaxUsed );
END Reset_Sequence_to_Data;
今、ガスで調理しています!
上記の手順では、テーブル内のフィールドの最大値を確認し、テーブルとフィールドのペアからシーケンス名を作成し、その最大値を使用して「Reset_Sequence」を呼び出します。
このパズルの最後のピースとケーキのアイシングが次に続きます...
create or replace
procedure Reset_All_Sequences
is
BEGIN
Reset_Sequence_to_Data( 'ACTIVITYLOG', 'LOGID' );
Reset_Sequence_to_Data( 'JOBSTATE', 'JOBID' );
Reset_Sequence_to_Data( 'BATCH', 'BATCHID' );
END Reset_All_Sequences;
実際のデータベースでは、このメカニズムを介してリセットされるシーケンスが約100あります。そのため、上記の手順では、Reset_Sequence_to_Dataへの呼び出しがさらに97回あります。
大好きです?嫌い?無関心?
l_current
可能性があることです。スクリプトが実行されるノードに応じて、さまざまな値のいずれかになる可能性があります。スクリプトを再実行すると、結果が異なる場合があります。複数回実行すると、最終的に特定の値に落ち着きました。
次のスクリプトは、シーケンスを目的の値に設定します。
PCS_PROJ_KEY_SEQという名前の新しく作成されたシーケンスとテーブルPCS_PROJがあるとします。
BEGIN
DECLARE
PROJ_KEY_MAX NUMBER := 0;
PROJ_KEY_CURRVAL NUMBER := 0;
BEGIN
SELECT MAX (PROJ_KEY) INTO PROJ_KEY_MAX FROM PCS_PROJ;
EXECUTE IMMEDIATE 'ALTER SEQUENCE PCS_PROJ_KEY_SEQ INCREMENT BY ' || PROJ_KEY_MAX;
SELECT PCS_PROJ_KEY_SEQ.NEXTVAL INTO PROJ_KEY_CURRVAL FROM DUAL;
EXECUTE IMMEDIATE 'ALTER SEQUENCE PCS_PROJ_KEY_SEQ INCREMENT BY 1';
END;
END;
/
END
キーワードがあります)。
このストアドプロシージャは私のシーケンスを再開します。
Create or Replace Procedure Reset_Sequence
is
SeqNbr Number;
begin
/* Reset Sequence 'seqXRef_RowID' to 0 */
Execute Immediate 'Select seqXRef.nextval from dual ' Into SeqNbr;
Execute Immediate 'Alter sequence seqXRef increment by - ' || TO_CHAR(SeqNbr) ;
Execute Immediate 'Select seqXRef.nextval from dual ' Into SeqNbr;
Execute Immediate 'Alter sequence seqXRef increment by 1';
END;
/
Oracleでシーケンスをリセットする別の方法があります。maxvalue
およびcycle
プロパティを設定します。nextval
シーケンスのがにヒットしたときに、プロパティが設定されているmaxvalue
場合はcycle
、minvalue
は、シーケンスのます。
ネガティブを設定することと比較したこの方法の利点 increment by
、リセットプロセスの実行中にシーケンスを引き続き使用できるため、リセットを実行するために何らかの形式の停止を行う必要がなくなるということです。
の値maxvalue
はcurrentより大きい必要があるnextval
ため、以下の手順にはオプションのパラメーターが含まれてnextval
おり、手順でを選択してからcycle
プロパティを設定するまでの間にシーケンスに再度アクセスした場合にバッファーを許可します。
create sequence s start with 1 increment by 1;
select s.nextval from dual
connect by level <= 20;
NEXTVAL
----------
1
...
20
create or replace procedure reset_sequence ( i_buffer in pls_integer default 0)
as
maxval pls_integer;
begin
maxval := s.nextval + greatest(i_buffer, 0); --ensure we don't go backwards!
execute immediate 'alter sequence s cycle minvalue 0 maxvalue ' || maxval;
maxval := s.nextval;
execute immediate 'alter sequence s nocycle maxvalue 99999999999999';
end;
/
show errors
exec reset_sequence;
select s.nextval from dual;
NEXTVAL
----------
1
現状の手順では、別のセッションが値0をフェッチする可能性がありますが、これは問題となる場合とそうでない場合があります。もしそうなら、あなたはいつでもできます:
minvalue 1
最初の変更で設定nextval
フェッチを除外するnocycle
プロパティを別のプロシージャに設定し、後で実行します(これを実行する場合)。Jezus、このすべてのプログラミングはインデックスの再起動のみです...おそらく私はばかですが、Oracle 12より前(再起動機能があります)の場合、simpelの何が問題になっていますか。
drop sequence blah;
create sequence blah
?
1)以下に示すようなシーケンスを作成するとします。
CREATE SEQUENCE TESTSEQ
INCREMENT BY 1
MINVALUE 1
MAXVALUE 500
NOCACHE
NOCYCLE
NOORDER
2)次に、SEQUENCEから値をフェッチします。以下のように4回フェッチしたとしましょう。
SELECT TESTSEQ.NEXTVAL FROM dual
SELECT TESTSEQ.NEXTVAL FROM dual
SELECT TESTSEQ.NEXTVAL FROM dual
SELECT TESTSEQ.NEXTVAL FROM dual
3)上記の4つのコマンドを実行した後、SEQUENCEの値は4になります。ここで、SEQUENCEの値を再び1にリセットしたとします。以下の手順に従ってください。以下に示すように、すべてのステップを同じ順序で実行します。
ALTER SEQUENCE TESTSEQ INCREMENT BY -3;
SELECT TESTSEQ.NEXTVAL FROM dual
ALTER SEQUENCE TESTSEQ INCREMENT BY 1;
SELECT TESTSEQ.NEXTVAL FROM dual
シーケンスのINCREMENT値を変更し、インクリメントしてから元に戻すのは非常に簡単です。さらに、シーケンスを削除または再作成した場合のように、すべての許可を再確立する必要がないという利点もあります。
すべてのシーケンスをリセットするブロックを作成します。
DECLARE
I_val number;
BEGIN
FOR US IN
(SELECT US.SEQUENCE_NAME FROM USER_SEQUENCES US)
LOOP
execute immediate 'select ' || US.SEQUENCE_NAME || '.nextval from dual' INTO l_val;
execute immediate 'alter sequence ' || US.SEQUENCE_NAME || ' increment by -' || l_val || ' minvalue 0';
execute immediate 'select ' || US.SEQUENCE_NAME || '.nextval from dual' INTO l_val;
execute immediate 'alter sequence ' || US.SEQUENCE_NAME || ' increment by 1 minvalue 0';
END LOOP;
END;
以下は、シーケンスによって返される次の値を変更するためのより堅牢な手順と、さらに多くの手順です。
next_value
!=になりますmin_value
との間min_value
とmax_value
。increment_by
クリーンアップ時に現在の(または提案された)設定とその他すべてのシーケンス設定を考慮に入れます。ORA-01403: no data found
エラーが発生します。これがコードです:
CREATE OR REPLACE PROCEDURE alter_sequence(
seq_name user_sequences.sequence_name%TYPE
, next_value user_sequences.last_number%TYPE := null
, increment_by user_sequences.increment_by%TYPE := null
, min_value user_sequences.min_value%TYPE := null
, max_value user_sequences.max_value%TYPE := null
, cycle_flag user_sequences.cycle_flag%TYPE := null
, cache_size user_sequences.cache_size%TYPE := null
, order_flag user_sequences.order_flag%TYPE := null)
AUTHID CURRENT_USER
AS
l_seq user_sequences%rowtype;
l_old_cache user_sequences.cache_size%TYPE;
l_next user_sequences.min_value%TYPE;
BEGIN
-- Get current sequence settings as defaults
SELECT * INTO l_seq FROM user_sequences WHERE sequence_name = seq_name;
-- Update target settings
l_old_cache := l_seq.cache_size;
l_seq.increment_by := nvl(increment_by, l_seq.increment_by);
l_seq.min_value := nvl(min_value, l_seq.min_value);
l_seq.max_value := nvl(max_value, l_seq.max_value);
l_seq.cycle_flag := nvl(cycle_flag, l_seq.cycle_flag);
l_seq.cache_size := nvl(cache_size, l_seq.cache_size);
l_seq.order_flag := nvl(order_flag, l_seq.order_flag);
IF next_value is NOT NULL THEN
-- Determine next value without exceeding limits
l_next := LEAST(GREATEST(next_value, l_seq.min_value+1),l_seq.max_value);
-- Grab the actual latest seq number
EXECUTE IMMEDIATE
'ALTER SEQUENCE '||l_seq.sequence_name
|| ' INCREMENT BY 1'
|| ' MINVALUE '||least(l_seq.min_value,l_seq.last_number-l_old_cache)
|| ' MAXVALUE '||greatest(l_seq.max_value,l_seq.last_number)
|| ' NOCACHE'
|| ' ORDER';
EXECUTE IMMEDIATE
'SELECT '||l_seq.sequence_name||'.NEXTVAL FROM DUAL'
INTO l_seq.last_number;
l_next := l_next-l_seq.last_number-1;
-- Reset the sequence number
IF l_next <> 0 THEN
EXECUTE IMMEDIATE
'ALTER SEQUENCE '||l_seq.sequence_name
|| ' INCREMENT BY '||l_next
|| ' MINVALUE '||least(l_seq.min_value,l_seq.last_number)
|| ' MAXVALUE '||greatest(l_seq.max_value,l_seq.last_number)
|| ' NOCACHE'
|| ' ORDER';
EXECUTE IMMEDIATE
'SELECT '||l_seq.sequence_name||'.NEXTVAL FROM DUAL'
INTO l_next;
END IF;
END IF;
-- Prepare Sequence for next use.
IF COALESCE( cycle_flag
, next_value
, increment_by
, min_value
, max_value
, cache_size
, order_flag) IS NOT NULL
THEN
EXECUTE IMMEDIATE
'ALTER SEQUENCE '||l_seq.sequence_name
|| ' INCREMENT BY '||l_seq.increment_by
|| ' MINVALUE '||l_seq.min_value
|| ' MAXVALUE '||l_seq.max_value
|| CASE l_seq.cycle_flag
WHEN 'Y' THEN ' CYCLE' ELSE ' NOCYCLE' END
|| CASE l_seq.cache_size
WHEN 0 THEN ' NOCACHE'
ELSE ' CACHE '||l_seq.cache_size END
|| CASE l_seq.order_flag
WHEN 'Y' THEN ' ORDER' ELSE ' NOORDER' END;
END IF;
END;
私のプロジェクトでは、シーケンスを使用せずに誰かが手動でレコードを入力したことが一度発生したため、シーケンス値を手動でリセットする必要があります。
declare
max_db_value number(10,0);
cur_seq_value number(10,0);
counter number(10,0);
difference number(10,0);
dummy_number number(10);
begin
-- enter table name here
select max(id) into max_db_value from persons;
-- enter sequence name here
select last_number into cur_seq_value from user_sequences where sequence_name = 'SEQ_PERSONS';
difference := max_db_value - cur_seq_value;
for counter in 1..difference
loop
-- change sequence name here as well
select SEQ_PERSONS.nextval into dummy_number from dual;
end loop;
end;
シーケンスが遅れている場合、上記のコードが機能することに注意してください。
以下に示すCYCLEオプションを使用できます。
CREATE SEQUENCE test_seq
MINVALUE 0
MAXVALUE 100
START WITH 0
INCREMENT BY 1
CYCLE;
この場合、シーケンスがMAXVALUE(100)に達すると、MINVALUE(0)にリサイクルされます。
減少したシーケンスの場合、シーケンスはMAXVALUEにリサイクルされます。
すべての自動インクリメントシーケンスを実際のデータと一致させる方法は次のとおりです。
このスレッドですでに説明されているように、次の値を強制するプロシージャを作成します。
CREATE OR REPLACE PROCEDURE Reset_Sequence(
P_Seq_Name IN VARCHAR2,
P_Val IN NUMBER DEFAULT 0)
IS
L_Current NUMBER := 0;
L_Difference NUMBER := 0;
L_Minvalue User_Sequences.Min_Value%Type := 0;
BEGIN
SELECT Min_Value
INTO L_Minvalue
FROM User_Sequences
WHERE Sequence_Name = P_Seq_Name;
EXECUTE Immediate 'select ' || P_Seq_Name || '.nextval from dual' INTO L_Current;
IF P_Val < L_Minvalue THEN
L_Difference := L_Minvalue - L_Current;
ELSE
L_Difference := P_Val - L_Current;
END IF;
IF L_Difference = 0 THEN
RETURN;
END IF;
EXECUTE Immediate 'alter sequence ' || P_Seq_Name || ' increment by ' || L_Difference || ' minvalue ' || L_Minvalue;
EXECUTE Immediate 'select ' || P_Seq_Name || '.nextval from dual' INTO L_Difference;
EXECUTE Immediate 'alter sequence ' || P_Seq_Name || ' increment by 1 minvalue ' || L_Minvalue;
END Reset_Sequence;
すべてのシーケンスを実際のコンテンツと一致させる別のプロシージャを作成します。
CREATE OR REPLACE PROCEDURE RESET_USER_SEQUENCES_TO_DATA
IS
STMT CLOB;
BEGIN
SELECT 'select ''BEGIN'' || chr(10) || x || chr(10) || ''END;'' FROM (select listagg(x, chr(10)) within group (order by null) x FROM ('
|| X
|| '))'
INTO STMT
FROM
(SELECT LISTAGG(X, ' union ') WITHIN GROUP (
ORDER BY NULL) X
FROM
(SELECT CHR(10)
|| 'select ''Reset_Sequence('''''
|| SEQ_NAME
|| ''''','' || coalesce(max('
|| COL_NAME
|| '), 0) || '');'' x from '
|| TABLE_NAME X
FROM
(SELECT TABLE_NAME,
REGEXP_SUBSTR(WTEXT, 'NEW\.(\S*) IS NULL',1,1,'i',1) COL_NAME,
REGEXP_SUBSTR(BTEXT, '(\.|\s)([a-z_]*)\.nextval',1,1,'i',2) SEQ_NAME
FROM USER_TRIGGERS
LEFT JOIN
(SELECT NAME BNAME,
TEXT BTEXT
FROM USER_SOURCE
WHERE TYPE = 'TRIGGER'
AND UPPER(TEXT) LIKE '%NEXTVAL%'
)
ON BNAME = TRIGGER_NAME
LEFT JOIN
(SELECT NAME WNAME,
TEXT WTEXT
FROM USER_SOURCE
WHERE TYPE = 'TRIGGER'
AND UPPER(TEXT) LIKE '%IS NULL%'
)
ON WNAME = TRIGGER_NAME
WHERE TRIGGER_TYPE = 'BEFORE EACH ROW'
AND TRIGGERING_EVENT = 'INSERT'
)
)
) ;
EXECUTE IMMEDIATE STMT INTO STMT;
--dbms_output.put_line(stmt);
EXECUTE IMMEDIATE STMT;
END RESET_USER_SEQUENCES_TO_DATA;
ノート:
ユーザーが値を知っている必要がない代替方法を作成します。システムは変数を取得して更新に使用します。
--Atualizando sequence da tabela SIGA_TRANSACAO, pois está desatualizada
DECLARE
actual_sequence_number INTEGER;
max_number_from_table INTEGER;
difference INTEGER;
BEGIN
SELECT [nome_da_sequence].nextval INTO actual_sequence_number FROM DUAL;
SELECT MAX([nome_da_coluna]) INTO max_number_from_table FROM [nome_da_tabela];
SELECT (max_number_from_table-actual_sequence_number) INTO difference FROM DUAL;
IF difference > 0 then
EXECUTE IMMEDIATE CONCAT('alter sequence [nome_da_sequence] increment by ', difference);
--aqui ele puxa o próximo valor usando o incremento necessário
SELECT [nome_da_sequence].nextval INTO actual_sequence_number from dual;
--aqui volta o incremento para 1, para que futuras inserções funcionem normalmente
EXECUTE IMMEDIATE 'ALTER SEQUENCE [nome_da_sequence] INCREMENT by 1';
DBMS_OUTPUT.put_line ('A sequence [nome_da_sequence] foi atualizada.');
ELSE
DBMS_OUTPUT.put_line ('A sequence [nome_da_sequence] NÃO foi atualizada, já estava OK!');
END IF;
END;
私のために働いたストアドプロシージャ
create or replace
procedure reset_sequence( p_seq_name in varchar2, tablename in varchar2 )
is
l_val number;
maxvalueid number;
begin
execute immediate 'select ' || p_seq_name || '.nextval from dual' INTO l_val;
execute immediate 'select max(id) from ' || tablename INTO maxvalueid;
execute immediate 'alter sequence ' || p_seq_name || ' increment by -' || l_val || ' minvalue 0';
execute immediate 'select ' || p_seq_name || '.nextval from dual' INTO l_val;
execute immediate 'alter sequence ' || p_seq_name || ' increment by '|| maxvalueid ||' minvalue 0';
execute immediate 'select ' || p_seq_name || '.nextval from dual' INTO l_val;
execute immediate 'alter sequence ' || p_seq_name || ' increment by 1 minvalue 0';
end;
ストアドプロシージャの使用方法:
execute reset_sequence('company_sequence','company');