Thomas Kramer

IT-COW | Anleitungen

Sequenzielle Einspielung von Oracle-Skripten sicherstellen

By Administrator at Juni 14, 2010 15:07
Filed Under: Administration, Anleitungen

SQL-Update-Skripte, die dem Zweck dienen eine Datenbank auf einen neueren Stand zu bringen, werden häufig noch von Hand ausgeführt. Damit nicht versehentlich die Reihenfolge durcheinandergebracht und das Datenbankschema damit geshreddert wird habe ich mir daher für die Sicherstellung der sequenziellen Abarbeitung folgendes überlegt - der Sinn liegt darin dieses Skript als "Präambel" für jedes SQL-Skript einzuführen:

 

set echo off;
SET SERVEROUTPUT ON FORMAT WRAPED;
DECLARE
  AKTUELLES_DBSCHEMA VARCHAR2(64);
  AKTUELLES_DBSCHEMA_INT INT;
  KUENFTIGES_DBSCHEMA VARCHAR2(64) := '3.75.0.1';
  KUENFTIGES_DBSCHEMA_INT INT;
  ABBRECHEN BOOLEAN := false;

  TYPE DB_Historie IS TABLE OF VARCHAR2(64) INDEX BY BINARY_INTEGER;
  DBHistorie DB_Historie;

  FUNCTION to_int (PARAMETER VARCHAR2)
  RETURN INT IS
    Rueckgabewert VARCHAR2(64) := '';
  BEGIN
    FOR i IN 1 .. Length(parameter) LOOP
      IF SubStr(parameter,i,1) <>'.' THEN
        Rueckgabewert := Rueckgabewert || SubStr(parameter,i,1);
      END IF;
    END LOOP;

    RETURN to_number(Rueckgabewert);
  END;
BEGIN
  select SPALTENNAME into AKTUELLES_DBSCHEMA from TABELLENNAME WHERE UPPER(SPALTENNAME) = 'DBVERSION';
  DBMS_OUTPUT.PUT_LINE ('Die aktuelle Datenbankschema-Version ist: ' || AKTUELLES_DBSCHEMA);
  AKTUELLES_DBSCHEMA_INT:=to_int(aktuelles_DBSchema);
  KUENFTIGES_DBSCHEMA_INT:= to_int(KUENFTIGES_DBSCHEMA);   
 
  IF KUENFTIGES_DBSCHEMA_INT<AKTUELLES_DBSCHEMA_INT THEN
    DBMS_OUTPUT.PUT_LINE ('Dieses Skript ist veraltet, Abarbeitung wird abgebrochen.');
    ABBRECHEN:=TRUE;  
  END IF;

  DBHISTORIE(4):='3.75.1.6'; 
  DBHISTORIE(3):='3.75.1.5';
  DBHISTORIE(2):='3.75.0.5';
  DBHISTORIE(1):='3.75.0.3';     
  DBHISTORIE(0):='3.75.0.1';
   
  FOR i IN 0 .. DBHistorie.Count-1 LOOP
    IF TO_INT(DBHISTORIE(I))>AKTUELLES_DBSCHEMA_INT THEN
      -- entweder alle Skripte auflisten, oder die die dazwischen liegen

      IF ((KUENFTIGES_DBSCHEMA_INT<AKTUELLES_DBSCHEMA_INT) and

               (TO_INT(DBHISTORIE(I))>AKTUELLES_DBSCHEMA_INT)) OR

         ((KUENFTIGES_DBSCHEMA_INT>AKTUELLES_DBSCHEMA_INT) and        

          (TO_INT(DBHISTORIE(I))>AKTUELLES_DBSCHEMA_INT) and

          (TO_INT(DBHISTORIE(I))<KUENFTIGES_DBSCHEMA_INT)) then
        IF (Abbrechen)  then      
          dbms_output.put_line ('Als nächstes bitte DB-Skript ' || DBHistorie(i) || ' ausführen.');
        ELSE     
          DBMS_OUTPUT.PUT_LINE ('Vorher bitte DB-Skript ' || DBHISTORIE(I) || ' ausführen.');
        END IF;
        ABBRECHEN := TRUE;
      end if;
    END IF;
  END LOOP;

  IF ABBRECHEN THEN
    dbms_output.put_line ('Bearbeitung des Skripts wurde abgebrochen.');
  ELSE
    -- hier sql-Befehle einfügen
    BEGIN
      DBMS_OUTPUT.PUT_LINE ('');
      COMMIT;
    EXCEPTION
      WHEN OTHERS THEN
        DBMS_OUTPUT.PUT_LINE ('Ein unbehandelter Fehler ist aufgetreten.');
      ROLLBACK;
    end;
  END IF;

  END;

 

Eigentlich recht einfach. Man könnte es sicher noch eleganter machen, aber es erfüllt seinen Zweck. Zur Erklärung:

 

Die Variable KUENFTIGES_DBSCHEMA legt fest auf welche Datenbankschema-Version dieses Skript die Datenbank setzt. Die aktuelle Datenbankschema-Version wird dagegen ausgelesen und in AKTUELLES_DBSCHEMA gespeichert. Das dynamische Array DBHISTORIE enthält alle vorangegangenen Schema-Versionen - die Liste müsste natürlich irgendwann schon gekürzt oder ausgelagert werden.

 

Wenn KUENFTIGES_DBSCHEMA kleiner als die aktuelle Schema-Version ist werden alle bekannten noch ausstehenden Schema-Versionen aufgelistet (eher unwahrscheinlich das es dann welche gibt...) und dann die Abarbeitung des Skripts abgebrochen. Wenn dagegen KUENFTIGES_DBSCHEMA größer als die aktuelle Schema-Version ist und wenn es Skripte gibt die vorher ausgeführt werden müssen, werden diese aufgelistet und das Skript beendet - ansonsten wird mit der Abarbeitung des Skripts fortgefahren.

 

Ergänzung 11.04.2011: Bei dem obigen Quellcode handelt es sich um PL/SQL, dem erweiterten SQL-Dialekt für Oracle-Datenbanken. Zur Einführung in das Thema verweise ich auf meinen früheren Reminder: Link.

 

Tag-Wolke

Monats-Liste