PostgreSQLでは、次のようなことができます。
ALTER SEQUENCE serial RESTART WITH 0;
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
別の良い議論もここにあります:シーケンスをリセットする方法?
真の再起動は可能ではありませんAFAIK。(私が間違っている場合は修正してください!)。
ただし、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;
私のアプローチは、Dougman の例を少し拡張したものです。
拡張子は...
シード値をパラメーターとして渡します。なんで?シーケンスをいくつかのテーブルで使用されている最大 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 回あります。
大好きです?嫌い?無関心?
次のスクリプトは、シーケンスを目的の値に設定します。
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;
/
このストアド プロシージャは、シーケンスを再開します。
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
は現在の値よりも大きくする必要があるため、以下の手順には、手順での選択とプロパティの設定の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
2番目のフェッチを除外しますnocycle
プロパティを別のプロシージャに設定し、後日実行できるようにします(これを実行する場合)。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
シーケンスによって返される次の値を変更するためのより堅牢な手順と、さらに多くの手順を次に示します。
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;
シーケンスの INCREMENT 値を変更し、インクリメントしてから元に戻す操作は非常に簡単です。さらに、シーケンスを削除/再作成した場合のように、すべての許可を再確立する必要がないという追加の利点があります。
すべての自動インクリメント シーケンスを実際のデータと一致させる方法は次のとおりです。
このスレッドで既に説明されているように、次の値を強制する手順を作成します。
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;
ノート:
私のために働いたストアドプロシージャ
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');