Jak automatycznie podbić sequencery?



Większość z nas w którymś momencie swojej kariery trafiła na bojowe zadanie - MIGRACJA DANYCH. Niektórzy lubią gmerać w danych a inni na samo słowo MIGRACJA dostają gęsiej skórki. Niemniej jednak MIGRACJE są nieodłącznym elementem bazodanowego krajobrazu i trzeba śmiało stawić czoła temu wyzwaniu!

Jednym z problemów, na które można się natknąć podczas przenoszenia danych to problem z sequencerami. Jeśli kopiujemy dane z jednej bazy na drugą, łącznie z ustawionymi na bazie źródłowej sequencerami, to na bazie docelowej w tabelach będziemy mieli rekordy z identyfikatorami większymi niż stan liczników na bazie. Próba wstawienia rekordu w takiej sytuacji skończy się błędem DUP_VAL_ON_INDEX.

Żeby doprowadzić system do względnej równowagi musimy podnieść wartości naszych sequencerów tak, by liczniki były większe niż maksymalne wartości w tabelach.

Niestety w Oracle nie ma powiązania bezpośredniego między tabelą i seqencerem więc nie jesteśmy w stanie automatycznie zweryfikować, do jakiej wartości podnieść liczniki. Najczęściej po prostu podbija się wszystkie liczniki do jakiejś dużej wartości, tak by mieć pewność, że liczniki będą większe od maksymalnego ID w każdej tabeli. Warto w tym celu też zweryfikować wartości ID w naszych największych tabelach.

Jak już ustalimy naszą nową wartość liczników to dalej już będzie prosto! Wystarczy podbić wszystkie liczniki do nowej wielkości....

Ręczne podbijanie liczników jest dość monotonne i czasochłonne. Więc może przygotujmy sobie narzędzie. 

W narzędziu skorzystamy z dwóch przydatnych funkcjonalności ORACLE.

SŁOWNIKI
w widokach słownikowych Oracle możemy znaleźć definicje obiektów (i nie tylko) naszej bazy danych. Dzisiaj będzie nam potrzebny słownik zawierający definicje seqencerów. Słownik ten to ALL_SEQUENCES.

EXECUTE IMMEDIATE
klauzula umożliwiająca nam tworzenie dynamicznych poleceń DDL (i nie tylko :))

Także zaczynajmy!

Dzięki słowniowi Oracle możemy się dowiedzieć, jakie mamy sequencery w bazie danych oraz jakie mają aktualne wartości. W bazie HR mamy trzy sequencery:


Załóżmy, że chcemy podbić je do wartości 100.
By to uczynić mamy dwie możliwości:

Pierwsza opcja to usunięcie i utworzenie ponownie sequencera z parametrem start with ustawioną na naszą oczekiwaną wartość. 

1:  DECLARE  
2:   N NUMBER := 100;  
3:  BEGIN  
4:  FOR Z IN (  
5:  SELECT SEQUENCE_NAME  
6:    FROM  all_sequences where sequence_owner = 'HR') LOOP  
7:     execute immediate 'DROP SEQUENCE '||'HR'||'.'||z.SEQUENCE_NAME;  
8:     execute immediate 'CREATE SEQUENCE '||'HR'||'.'||z.SEQUENCE_NAME ||' START WITH ' || N ||' INCREMENT BY 1' ;   
9:    END LOOP;  
10:   END;  

Po wykonaniu skryptu nasze liczniki powinny ustawić się na oczekiwane wartości. Sprawdźmy!


Udało się!

Drugą metodą, jest podkręcenie licznika przez zmianę licznika i podbicie parametru INCREMENT BY. Jak  to zrobić?
Skrypt jest bardzo podobny do pierwszego.

1:  set serveroutput on  
2:  declare  
3:  nSeqNew number := 100;  
4:  vOwner varchar2(30) := 'HR';  
5:  nseqTest number;  
6:  begin   
7:    for z in (  
8:    SELECT (nSeqNew - last_number) seqInc, SEQUENCE_NAME  
9:    FROM  all_sequences where sequence_owner = vOwner) loop  
10:     execute immediate 'ALTER SEQUENCE '||vOwner||'.'||z.SEQUENCE_NAME ||' INCREMENT BY ' || z.seqInc;  
11:     execute immediate 'select '||vOwner||'.'||z.SEQUENCE_NAME || '.nextval from dual' into nseqTest;  
12:     execute immediate 'ALTER SEQUENCE '||vOwner||'.'||z.SEQUENCE_NAME ||' INCREMENT BY 1';  
13:     dbms_output.put_line(z.SEQUENCE_NAME || ' new '||nseqTest);  
14:   end loop;  
15:  end;  

Czy zadziała? Zerknijmy...


I znów się udało :)


Dzięki słownikom oraz poleceniu EXECUTE IMMEDIATE migracja danych nie jest taka straszna!

Komentarze