If-Abfrage in SQL-Skript-Datei

Tilman Räger

Benutzer
Beiträge
10
Hallo,
ich habe ein kleines Problem: Ich soll eine bestehende Tabelle mit Hilfe einer .sql-Skript-Datei ändern, vorher allerdings überprüfen, ob diese Änderung nicht schon durchgeführt wurde. Dabei ist die eigentliche Änderung kein Problem, wichtig ist mir, wie ich diese Abfrage hineinbekomme. Als Pseudocode also in etwa

if (<Feld x nicht vorhanden) {
DROP INDEX Idx //
ALTER TABLE ... // rename der alten Tabelle
CREATE TABLE ... // neu erstellen der neuen Tabelle
INSERT ... // Einlesen der Daten aus der alten Tabelle und einfügen in die neue
}

Leider stehe ich hier ein wenig auf dem Schlauch. Normalerweise würde ich so etwas in C++ machen, aber hier ist nun einmal SQL vorgegeben. Meine SQL-Ausbildung liegt schon einige Zeit zurück und ich bin auch nicht sicher, ob damals so etwas überhaupt möglich war. Was ich im Internet über prozedurales SQL gefunden habe (ich denke mal, das das in etwa so etwas ist) war auch nicht gerade erheiternd - da hieß es, DDL-Befehle (also ALTER TABLE etc.) wären nicht erlaubt :-(
Vielleicht kann mir ja jemand einen Tip geben.
Im Voraus schon einmal vielen Dank

Tilman (Räger)
 
Werbung:
Was hast Du denn genau vor? So wie es dasteht hast Du zum Schluß einen Index weniger und eine Tabelle mehr. Dein if ... scheint zu signalisieren, daß Du die Tabelle lediglich ändern willst, also ein Feld zufügen. Das ginge auch via ALTER TABLE.
 
Hallo,
Vergiss das, was innerhalb der gescheiften Klammern steht - dieser Programmteil läuft bereits erfolgreich. Das Problem ist, die Tabelle nur dann zu ändern (bzw. diesen ganzen Block nur dann auszuführen) wenn das beim CREATE TABLE neu hinzugefügte Feld noch nicht existiert. Da dieses Feld zum einen mit 'not NULL' befrachtet, jedoch keinen Defaultwert haben soll, ist m.W. ein direktes Ändern der Tabelle mittels ALTER TABLE nicht möglich, das ich zudem noch ein CREATE INDEX vergessen habe, ist mir auch schon aufgefallen - aber wie gesagt, darum geht es nicht. Wichtig ist, wie ich SQL dazu bekomme, diese Befehle nur auszuführen, wenn das fragliche Feld noch nicht existiert und ansonsten den ganzen Block zu ignorieren.

Tilman
 
In PG würde ich dann eine Stored Procedure schreiben. Ob das Feld da ist, siehst Du in information_schema.columns (das ist ein standardisierter View über alles Tabellen und deren Spalten, da es wohl sogar MySQL kann stehen die Chance gut, daß es auch SQLite kann ...) Ich habe aber von SQLite zu wenig Erfahrung/Wissen, um einschätzen zu können, ob das da mit stored Procs und so weiter alles geht ...
 
Mal so als Fingerübung als Inline-Function in PostgreSQL:

Code:
--
-- unsere Ausgangstabelle
--
test=# \d tilman;
  Tabelle »public.tilman«
 Spalte |  Typ  | Attribute
--------+---------+-----------
 id  | integer | not null
 val  | text  |
Indexe:
  "tilman_pkey" PRIMARY KEY, btree (id)

--
-- jetzt, in einer Inline-Funktion (DO-Statement) prüfen, ob Spalte neue_spalte (int) schon da ist, falls nein, anlegen
--
test=# do $$declare c int;begin select count(*) from information_schema.columns where table_name = 'tilman' and column_name='neue_spalte' into c; if c=0 then alter table tilman add column neue_spalte int default 10; end if; end$$;
DO

--
-- prüfen
--
test=*# \d tilman;
  Tabelle »public.tilman«
  Spalte  |  Typ  |  Attribute   
-------------+---------+----------------
 id  | integer | not null
 val  | text  |
 neue_spalte | integer | Vorgabewert 10
Indexe:
  "tilman_pkey" PRIMARY KEY, btree (id)

--
-- wir rufen diesen DO-Block noch mal auf, diesmal ist die Spalte aber schon da, es sollte ohne Fehler laufen, aber nix machen
--
test=*# do $$declare c int;begin select count(*) from information_schema.columns where table_name = 'tilman' and column_name='neue_spalte' into c; if c=0 then alter table tilman add column neue_spalte int default 10; end if; end$$;
DO
test=*# \d tilman;
  Tabelle »public.tilman«
  Spalte  |  Typ  |  Attribute   
-------------+---------+----------------
 id  | integer | not null
 val  | text  |
 neue_spalte | integer | Vorgabewert 10
Indexe:
  "tilman_pkey" PRIMARY KEY, btree (id)

--
-- keine Fehler, keine neue Spalte, niemand wurde verletzt
--
test=*#
 
Hallo,

Mit Stored Procedures kenne ich mich bisher nicht aus - allerdings weiss ich auch nicht, ob das zielführend ist. Das Skript soll im Rahmen eines Programmupdates mit Änderung der zugrundeliegenden Datenbank eingesetzt werden, also einmal!
Mein Problem nun ist, das das einzige Konstrukt der Art:
IF <Bedingung> THEN
BEGIN
....
END

den eindeutigen Vermerk trug: Keine DDL-Befehle. Die Frage ist also weiterhin: Gibt es eine Konstruktion, in der ich eine entsprechend Abfrage machen kann und dann im If-Zweig DDL-Befehle aufrufen kann. Oder stehe ich einfach nur mal wieder auf der Leitung?

Gruss
Tilman (Räger)
 
Mein Problem nun ist, das das einzige Konstrukt der Art:
IF <Bedingung> THEN
BEGIN
....
END

Erst einmal kennt SQL kein IF <Bedingung> THEN. Das Problem mit DDL dürfte sein, daß fast alle Datenbanken bis auf eine (ihr dürft raten, welche ;-) ) kein DDL innherhalb von Transaktionen kann, und BEGIN startet eine Transaktion. Damit dürfte dann entweder die Transaktion automatisch an der Stelle commited werden oder abbrechen.

Warum willst Du alles innerhalb EINER Datei machen? Bau doch ein Shell-Script, was erst einmal prüft, ob die Spalte da ist, und dann, falls nicht, diese SQL-Befehle ausführt. Ich denke, damit kommst Du schneller ans Ziel.

Hrm. Du hast SQLite. Vor einiger Zeit trieb sich hier auch mal ein SQLite-Freak rum...
 
Problem ist, das mir die Form vorgegeben ist. Ich sagte ja, wenn ich es normal machen würde, würde ich einfach ein Programm in C++ oder Java schreiben und gut ist. Aber das Programm ruft beim Update diese SQL-Dateien mit vorgegebenem Namen auf (wie genau, kann ich nicht sagen - nicht meine Baustelle) und da ist die Vorgabe nun einmal SQL. Ob Shellskripte möglich sind, müsste ich allerdings noch mal nachfragen (Vielleicht sind sie ja prinzipiell möglich, wurden nur bisher nie verwendet.

Tilman
 
Ok, habe einen Weg gefunden: ich gehe die umbenannte Original-Tabelle ändern indem ich die Spalte mit Defaultwert hinzufüge, dann erzeuge ich die neue Tabelle ohne Default und kopiere um. Wenn die Spalte vorher schon exisitert hat, schlägt der ALTER TABLE Befehl und sqlite fährt mit dem nächsten Befehl fort. Zwar nicht besonders elegant, aber funktioniert :)

Tilman (Räger)
 
Werbung:
Hallo,
leider muss ich das Thema noch einmal fortführen, da der Weg mit dem fehlschlagenden ALTER TABLE Befehl folgende Datenbank-Updates blockiert. Das Problem ist wie folgt:
Die (nicht änderbare) Update-Routine überprüft die aktuelle Datenbank-Version und führt dann für jedes Update, das zwischen dieser und der benötigten Version liegt, ein SQL-Skript aus. Schlägt dieses fehl, so werden die folgenden Skripte nicht ausgeführt und die Datenbank-Version auch nicht hochgezählt, auch wenn das Skript nicht abgebrochen hat, sondern einfach nur einen Fehler gemeldet und weitergemacht hat. Ich benötige also einen SQL-Weg (bzw. alle Befehle, die mittels sqlite3 ausgeführt werden können) um festzustellen, ob das neu zu erstellende Feld schon exisitiert und ansonsten dieses einzufügen.

aktuell gehe ich so vor:

ALTER TABLE table_to_change RENAME TO table_bak;

// ich benötige für die Zwischenversion einen Default-Wert, der später allerdings nicht mehr auftreten darf
ALTER TABLE table_bak ADD COLUMN NewField INT NOT NULL DEFAULT 0;

CREATE table_to_change (
.... //alte Felddefinitionen
NewField INT NOT NULL
);
// Umkopieren von table_bak nach table_to_change

Ist das Feld schon vorhanden, so wird aktuell ein Fehler gemeldet und das Skript macht mit der nächsten Zeile weiter. Was ich allerdings übersehen hatte ist, das als Rückgabewert offensichtlich ein Fehlercode gemeldet wird - und dieser wohl von unserer Updateroutine ausgewertet wird mit der Folge, das keine weiteren Skripte mehr ausgeführt werden.
Ideal wäre also eine Möglichkeit, mit der so etwas verwirklichen kann:

if <Feld NewField nicht vorhanden> {
ALTER TABLE
 
Zurück
Oben