Announcement

Collapse
No announcement yet.

SQL-Script in Delphi ausführen

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • SQL-Script in Delphi ausführen

    Hallo !<p>Ich möchte ein SQL-Script in Delphi ausführen.<br>Ich habe das mit TQuery probiert, aber es gibt dort einige Probleme:<br>
    1. TQuery verarbeitet nur jeweils einen Befehl, man muß es für jeden Befehl des Scripts neu aufrufen.<br>
    2. TQuery versteht nicht alle Befehle (z.B. set autoddl)<br>
    3. mit TQuery ist es nicht möglich CREATE DATABASE auszuführen.<p>
    Ich habe versucht ISQL.EXE über Parameter per Programm aufzurufen, das hat aber nicht funktioniert, da man wohl keine Scriptdatei als Parameter übergeben kann, sondern nur eine Datenbank.<p>
    Es gibt doch bestimmt eine Unit oder eine Komponente,die kaum was kosten sollte und die ein IB-Script ausführt !?<p>
    Vielen Dank,<p>Marcus

  • #2
    Hallo,

    zu Punkt 1: <br>
    Diese Aussage ist so nicht korrekt, denn bei SQL-Datenbanken, die von Haus aus den Batch-Betrieb unterstützen (wie beim Microsoft SQL Server 7) können gleich mehrere Anweisungen auf ein Mal abgeschickt werden. Allerdings steht beim InterBase dieses Merkmal nicht zur Verfügung, so dass jede Anweisung einzeln abgeschickt werden muss.

    zu Punkt 2: <br>
    Man muss hier zwischen den "echten" SQL-Anweisungen (die zum InterBase geschickt werden) und den "Steueranweisungen" für WISQL (die nur innerhalb dieses Tools ausgewertet werden) unterscheiden. Der InterBase kennt natürlich die WISQL-internen Steueranweisungen nicht, so dass man beim Aufruf eine Fehlermeldung erhält.

    zu Punkt 3: <br>
    Eine TQuery benötigt eine <b>bestehende</b> Datenbankverbindung, da jede SQL-Anweisung vor dem Ausführen von der Datenbank vorbereitet wird. Allerdings bedeutet dies auch, dass eine neue Datenbank auf diesem Weg niemals erzeugt werden kann, da zum Zeitpunkt des Aufrufs die Datenbank noch nicht vorhanden ist. Aus diesem Grund sieht das <b>InterBase-API</b> auch eine spezielle API-Funktion (isc_dsql_execute_immediate) vor, um eine neue Datenbank zu erzeugen:
    <pre>
    procedure CreateDatabase(DBName, User, Pswd: string);
    var
    status: array[1..19] of LongInt;
    db, tran: PLongInt;
    rslt: LongInt;
    BaseSQL: String;
    begin
    db := nil;
    tran := nil;
    BaseSQL := 'CREATE DATABASE "' + DBName +'" USER "' + User
    + '" PASSWORD ' + '"' + Pswd + '" '
    + 'DEFAULT CHARACTER SET ISO8859_1';
    rslt := isc_dsql_execute_immediate(@status, db,
    tran, 0, PChar(BaseSQL), 1, nil);
    if rslt <> 0 then
    raise EDatabaseError.Create(
    'Fehler beim Erstellen der Datenbank!' +
    #10#13 + 'ISC-Nr.: ' + IntToStr(rslt) +
    #10#13 + FormulateError(rslt, @status));
    rslt := isc_detach_database(@status, db);
    if rslt <> 0 then
    raise EDatabaseError.Create(
    'Fehler beim Trennen der Verbindung!' +
    #10#13 + 'ISC-Nr.: ' + IntToStr(rslt) +
    #10#13 + FormulateError(rslt, @status));
    end;
    </pre>
    Im Fall von Delphi 5 ist der direkte Zugriff auf das InterBase-API nicht mehr notwendig, da <b>TIBDatabase</b> die Methode <b>CreateDatabase</b> anbietet:
    <pre>
    procedure TMainForm.BtnCreateDBClick(Sender: TObject);
    const
    cCreateTxt = ' %d. Versuch: Datenbank wird erstellt...';
    cReadyTxt = 'Datenbank wurde erstellt!';
    begin
    Screen.Cursor := crHourGlass;
    try
    try
    StatBar.SimpleText := Format(cCreateTxt, [1]);
    Refresh;
    with IBDatabase1 do
    begin
    DatabaseName := EditDBPath.Text;
    Params.Add(Format('USER "%s"', [EditUserName.Text]));
    Params.Add(Format('PASSWORD "%s"', [EditPassword.Text]));
    Params.Add('PAGE_SIZE 4096');
    Params.Add('DEFAULT CHARACTER SET ISO8859_1');
    CreateDatabase;
    end;
    StatBar.SimpleText := cReadyTxt;
    BtnCreateTable.Enabled := True;
    BtnCreateDB.Enabled := False;
    except
    on E:EDatabaseError do
    begin
    MessageBeep(MB_ICONEXCLAMATION);
    MessageDlg(E.Message, mtError, [mbOk], 0);
    end;
    end;
    finally
    Screen.Cursor := crDefault;
    end;
    end;
    </pre>
    P.S: Alle Beispiele stammen aus meinem Buch <i>Client/Server-Datenbankentwicklung mit Delphi</i>, die Details können dort nachgelesen werden.
    &#10

    Comment


    • #3
      Hallo Andreas !<p>
      Vielen Dank für Deine Antwort. Ich habe Dein Buch und bin nun dabei eine Komponente zu entwickeln, die Interbase-SQL-Scripts ausführt.<br>
      Das Programm macht folgendes ohne Probleme:<p>
      1. Übergabe eines TStrings an die Komponente<br>
      2. Filtern jedes einzelnen SQL-Befehls und diesen übertragen in eine andere Stringliste als jeweils neuen String (wobei nach Semikolon als Trennzeichen gesucht wird und auch das Umschalten mit SET TERM auf ein anderes Zeichen schon funktioniert)<br>
      3. Dabei werden auch überflüssige Leerzeichen entfernt (die Formatierung geht dabei allerdings verloren) und auch CR+LFs<br>
      4. AUTODDL + COMMIT wird ignoriert.<br>
      5. Beim Finden von CREATE DATABASE werden die Parameter an eine Deiner entsprechenden Funktion übergeben und aufgerufen.<br>
      6. Über TDatabase wird eine Verbindung mit der neuen Datenbank hergestellt und Databasename:='INTERN' gesetzt<br>
      7. Die neue StringListe enthält jetzt für TQuery ausführbare Befehle und TQuery wird an TDatabase (Databasename:='INTERN') angebunden, und in einer Schleife werden alle Strings der neuen StringListe an TQUERY.SQL.TEXT übergeben und EXECSQL aufgerufen.<br>
      8. Es läuft alles einwandfrei: Deklaration von UDFs, CREATE TABLE, GENERATOREN, TRIGGER<br>
      9. TQUERY bricht ab mit der Fehlermeldung:<p>
      Allgemeiner SQL-Fehler<br>
      Token unknown - line 1, char 229<br>
      ?.<p>
      Es ist eine Stored Procedure:<p>
      create procedure suche_naechstes_gewicht (EINLAEUFER integer, EINDATUM date) returns (DATUM1 date) as begin for select datum,gewicht from laufdaten where (laeufer=:einlaeufer) and (datum<=:eindatum) and ((gewicht is not null) or (gewicht>0)) into :datum1 do suspend; end<p>
      Die angegebene Position ist beim letzten OR.<br>
      Ich habe eine andere,kürzere Stored Procedure ausprobiert und das funktioniert. Ist diese vielleicht zu lang ? Alles ist ja eine Zeile.<p>Ich hoffe, Sie oder ein anderer hat eine Lösung, denn dann wäre mein Programm schon fast fertig !<p>
      Gruß Marcu

      Comment


      • #4
        Ich bins nochmal ! Das stimmt doch nicht !<p>
        Die abgedruckte Stored Procedure ist schon die kürzere Version und es klappt auch nicht.<p>
        Hier ist die lange, obwohl das wohl nicht weiterhilft:<p>
        alter procedure suche_naechstes_gewicht (EINLAEUFER integer, EINDATUM date) returns (GEWICHTGEFUNDEN float, DATUM1 date, DATUM2 date, GEWICHT1 float, GEWICHT2 float, TAGE1 integer, TAGE2 integer) as begin for select datum,gewicht from laufdaten where (laeufer=:einlaeufer) and (datum<=:eindatum) and ((gewicht is not null) or (gewicht>0)) order by datum into :datum1,:gewicht1 do suspend; for select datum,gewicht from laufdaten where (laeufer=:einlaeufer) and (datum>:eindatum) and ((gewicht is not null) or (gewicht>0)) order by datum desc into :datum2,:gewicht2 do suspend; tage1=F_AGEINDAYS(datum1,eindatum); tage2=F_AGEINDAYS(eindatum,datum2); if (tage2<tage1) then GEWICHTGEFUNDEN=gewicht2; else GEWICHTGEFUNDEN=gewicht1; suspend; end<p>
        Gruß Marcu

        Comment


        • #5
          Hallo,

          können die Stored Procedures in der Datenbank erfolgreich über <b>WISQL</b> bzw. <b>IBConsole</b> (die offiziellen InterBase-Tools) angelegt werden

          Comment


          • #6
            Hallo<p>
            Ich habe mit einer einfachen Testumgebung mit Delphi 5 (IBDatabase+IBTransaction+IBQuery, mit Standardeinstellungen) und LIBS V5.5 den problematischen Ausdruck über IBQUERY.EXECSQL ausgeführt und es kommt genau die gleiche Fehlermeldung ! <p>
            Dynamic SQL error<br>
            SQL error code=-104<br>
            Token unknown - lin 1,char 341<br>
            ?.<p>
            Mit WISQL läuft das aber.<p>
            Der Ausdruck war:<p><b>
            alter procedure suche_naechstes_gewicht (EINLAEUFER integer, EINDATUM date) returns (GEWICHTGEFUNDEN float, DATUM1 date, DATUM2 date, GEWICHT1 float, GEWICHT2 float, TAGE1 integer, TAGE2 integer) as begin for select datum,gewicht from laufdaten where (laeufer=:einlaeufer) and (datum<=:eindatum) and ((gewicht is not null) or (gewicht>0)) order by datum into :datum1,:gewicht1 do suspend; for select datum,gewicht from laufdaten where (laeufer=:einlaeufer) and (datum>:eindatum) and ((gewicht is not null) or (gewicht>0)) order by datum desc into :datum2,:gewicht2 do suspend; tage1=F_AGEINDAYS(datum1,eindatum); tage2=F_AGEINDAYS(eindatum,datum2); if (tage2<tage1) then GEWICHTGEFUNDEN=gewicht2; else GEWICHTGEFUNDEN=gewicht1; suspend; end </b><p>Gruß Marcu

            Comment


            • #7
              Hallo<p>
              Ich habe die Ursache des Problems gefunden und nun läuft alles wunderbar:<p>
              In der Delphi-Hilfe gibt es ein Beispiel zum Senden von CREATE PROCEDURE mit einem TQuery. Man muß dazu <b>Query.ParamCheck:=False</b> einstellen.<p>
              Das ist alles !<p>Gruß Marcu

              Comment

              Working...
              X