In PostgreSQL , böyle bir şey yapabilirsiniz:
ALTER SEQUENCE serial RESTART WITH 0;
Oracle eşdeğeri var mı?
In PostgreSQL , böyle bir şey yapabilirsiniz:
ALTER SEQUENCE serial RESTART WITH 0;
Oracle eşdeğeri var mı?
Yanıtlar:
Oracle guru Tom Kyte'den herhangi bir diziyi 0'a sıfırlamak için iyi bir prosedür . Aşağıdaki bağlantılarda da artıları ve eksileri hakkında büyük tartışma.
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;
/
Bu sayfadan: Sıra değerini sıfırlamak için dinamik SQL
İyi bir tartışma da burada: Sıralar nasıl sıfırlanır?
execute immediate
en fazla 1 satır döndüren bir seçimin çıktısını yakalamak için kullanıldığında sözdizimidir . Hemen yürütmeyle ilgili belgeler: docs.oracle.com/cd/B28359_01/appdev.111/b28370/…
'alter sequence ' || p_seq_name || ' increment by 1 minvalue 0';
Gerçek bir yeniden başlatma mümkün değildir AFAIK . (Yanılıyorsam lütfen beni düzeltin!).
Ancak, 0 olarak ayarlamak isterseniz, silebilir ve yeniden oluşturabilirsiniz.
Belirli bir değere ayarlamak istiyorsanız, INCREMENT öğesini negatif bir değere ayarlayabilir ve sonraki değeri alabilirsiniz.
Yani, diziniz 500 ise, 100 ile ayarlayabilirsiniz.
ALTER SEQUENCE serial INCREMENT BY -400;
SELECT serial.NEXTVAL FROM dual;
ALTER SEQUENCE serial INCREMENT BY 1;
Bu benim yaklaşımım:
Misal:
--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;
Bu özellik resmi olarak 18c'de eklendi, ancak 12.1'de gayri resmi olarak kullanılabilir.
Bu belgesiz özelliğin 12.1'de kullanılması tartışmalıdır. Sözdizimi resmi belgelere dahil edilmemiş olsa da , Oracle paketi DBMS_METADATA_DIFF tarafından oluşturulur . Üretim sistemlerinde birkaç kez kullandım. Ancak, bir Oracle Service isteği oluşturdum ve bunun bir dokümantasyon hatası olmadığını doğruladım, özellik gerçekten desteklenmiyor.
18c'de, özellik SQL Dil Sözdiziminde görünmez, ancak Veritabanı Yöneticisi Kılavuzu'nda bulunur .
... RESTART START WITH 0 MINVALUE 0
Benim yaklaşımım Dougman'ın örneğine bir gençlik uzantısı .
Uzantılar ...
Tohum değerini parametre olarak iletin. Neden? Bazı tabloda kullanılan maksimum kimliğe geri sıfırlama şey çağırmak istiyorum . Ben benzersiz bir tanımlayıcı için dizinin değerini kullandığım birincil anahtar ihlallerine neden olmayacak kadar yüksek bir seviyeye geri sonraki sıfırlama, bir dizi dizi için birden fazla çağrı yürüten başka bir komut dosyası bu proc çağırmak sonunda.
Ayrıca önceki minvalü onurlandırır . Aslında , istenen p_val veya mevcut minvalue mevcut veya hesaplanan bir sonraki değerden yüksekse bir sonraki değeri daha da yükseltebilir .
Hepsinden iyisi, belirtilen bir değere sıfırlamak için çağrılabilir ve sadece sarıcı sonunda "tüm dizilerimi düzelt" prosedürünü görene kadar bekleyin.
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;
Bu prosedür tek başına yararlıdır, ancak şimdi onu çağıran ve her şeyi programlı olarak bir dizi adlandırma kuralıyla ve mevcut bir tabloda / alanda kullanılan maksimum değeri arayarak belirten bir tane daha ekleyelim ...
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;
Şimdi gazla pişiriyoruz!
Yukarıdaki prosedür bir tablodaki alanın maksimum değerini kontrol eder, tablo / alan çiftinden bir sıra adı oluşturur ve algılanan maksimum değerle "Reset_Sequence" ı çağırır .
Bu bulmacanın son parçası ve pastanın üzerine krema geliyor ...
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;
Gerçek veritabanımda, bu mekanizma üzerinden sıfırlanan yaklaşık yüz sıra daha var, bu nedenle yukarıdaki prosedürde Reset_Sequence_to_Data öğesine 97 çağrı daha var .
Sevdim? Nefret ettin mi? Kayıtsız?
l_current
komut dosyasının hangi düğüme çalıştırıldığına bağlı olarak çeşitli değerlerden biri olabileceği bir RAC sisteminde öngörülemeyen davranışlara neden olabilmesidir; betiğin yeniden çalıştırılması farklı sonuçlara neden olabilir. Birden fazla kez çalıştırırsam sonunda belirli bir değere yerleştiğini buldum.
Aşağıdaki komut dizisi diziyi istenen bir değere ayarlar:
PCS_PROJ_KEY_SEQ ve tablo PCS_PROJ adlı yeni oluşturulmuş bir dizi verildiğinde:
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
anahtar kelime var).
Bu saklı yordam dizimi yeniden başlatır:
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'da bir sırayı sıfırlamanın başka bir yolu daha vardır: maxvalue
ve cycle
özelliklerini ayarlayın . Ne zaman nextval
dizinin vurur maxvalue
eğer, cycle
mülkiyet sonra hazır durumda tekrar başlayacakminvalue
dizisinin.
Bu yöntemin negatif ayarlamaya kıyasla avantajı increment by
, sıfırlama işlemi çalışırken dizinin kullanılmaya devam edebilmesidir ve sıfırlamayı yapmak için bir tür kesinti alma şansını azaltır.
İçin değer maxvalue
akımdan daha büyük olmalıdır nextval
, bu nedenle aşağıdaki prosedür, prosedürde seçimi seçmek nextval
ve cycle
özelliği ayarlamak arasında diziye tekrar erişilmesi durumunda bir ara belleğe izin veren isteğe bağlı bir parametre içerir .
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
Stand olarak prosedür hala başka bir oturumun 0 değerini getirmesine izin verir, bu da sizin için bir sorun olabilir veya olmayabilir. Öyleyse, her zaman şunları yapabilirsiniz:
minvalue 1
ilk Alternextval
getirmeyi hariç tutnocycle
Özelliği daha sonraki bir tarihte çalıştırılacak başka bir yordama ayarlamak için ifadeyi taşıyın (bunu yapmak istediğinizi varsayarsak).Jezus, sadece bir indeks yeniden başlatma için tüm bu programlama ... Belki de bir aptalım, ama oracle öncesi 12 (yeniden başlatma özelliği olan) için, bir basitlikte yanlış olan şey:
drop sequence blah;
create sequence blah
?
1) Aşağıda gösterildiği gibi bir SEQUENCE oluşturduğunuzu varsayalım:
CREATE SEQUENCE TESTSEQ
INCREMENT BY 1
MINVALUE 1
MAXVALUE 500
NOCACHE
NOCYCLE
NOORDER
2) Şimdi SEQUENCE içindeki değerleri getiriyorsunuz. Diyelim ki aşağıda gösterildiği gibi dört kez getirdim.
SELECT TESTSEQ.NEXTVAL FROM dual
SELECT TESTSEQ.NEXTVAL FROM dual
SELECT TESTSEQ.NEXTVAL FROM dual
SELECT TESTSEQ.NEXTVAL FROM dual
3) Yukarıdaki dört komutu uyguladıktan sonra, SIRA değeri 4 olacaktır. Şimdi SIRA değerini 1'e tekrar sıfırladığımı varsayalım. Aşağıdaki adımları izleyin. Tüm adımları aşağıda gösterilen sırayla uygulayın:
ALTER SEQUENCE TESTSEQ INCREMENT BY -3;
SELECT TESTSEQ.NEXTVAL FROM dual
ALTER SEQUENCE TESTSEQ INCREMENT BY 1;
SELECT TESTSEQ.NEXTVAL FROM dual
Sekansın INCREMENT değerini değiştirmek, artırmak ve sonra geri değiştirmek oldukça acısızdır, ayrıca sekansı bıraktığınız / yeniden oluşturduğunuz gibi tüm hibeleri yeniden kurmak zorunda kalmamanız da bir avantajdır.
Tüm dizilerimi sıfırlamak için bir blok oluşturuyorum:
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;
İşte bir dizi tarafından döndürülen bir sonraki değeri değiştirmek için daha sağlam bir prosedür ve çok daha fazlası.
next_value
! Olacak = olacak min_value
ve aralarında min_value
ve max_value
.increment_by
olarak, temizlik sırasında geçerli (veya önerilen) ayarı ve diğer tüm sıra ayarlarını dikkate alır.ORA-01403: no data found
hata ortaya çıkar.İşte kod:
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;
Benim projemde, birisi kayıtlar diziyi kullanmadan manuel olarak girdiğinde, bu yüzden sql kod snippet'inin altına yazdığım dizi değerini manuel olarak sıfırlamam gerekiyor:
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;
Sıralamanın gecikmesi durumunda yukarıdaki kodun çalışacağını lütfen unutmayın.
Aşağıda gösterilen ÇEVRİMİ seçeneğini kullanabilirsiniz:
CREATE SEQUENCE test_seq
MINVALUE 0
MAXVALUE 100
START WITH 0
INCREMENT BY 1
CYCLE;
Bu durumda, dizi MAXVALUE (100) değerine ulaştığında MINVALUE (0) değerine geri dönüştürülecektir.
Azalan bir dizi durumunda, dizi MAXVALUE değerine geri dönüştürülür.
Tüm otomatik artış sekanslarının gerçek verilerle nasıl eşleştirileceği aşağıda açıklanmıştır:
Bu iş parçacığında zaten açıklandığı gibi bir sonraki değeri zorlamak için bir yordam oluşturun:
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;
Tüm sekansları gerçek içerikle uzlaştırmak için başka bir prosedür oluşturun:
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;
NOTLAR:
Ben kullanıcının değerleri bilmek gerekmez bir alternatif yapmak, sistem almak ve güncellemek için değişkenleri kullanın.
--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;
Benim için çalışan saklı yordam
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;
Saklı yordam nasıl kullanılır:
execute reset_sequence('company_sequence','company');