[Gelöst] Datei mit SQL Statements zerteilen.

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Antworten
charlytango
Beiträge: 199
Registriert: Sa 12. Sep 2015, 12:10
OS, Lazarus, FPC: Laz 2.0 fixes FPC 3.2 fixes
CPU-Target: Win 32Bit, 64bit
Wohnort: Wien

[Gelöst] Datei mit SQL Statements zerteilen.

Beitrag von charlytango »

Hi,

ich muss eine Datei mit (mehrzeiligen) SQL Staements zerteilen um die Statements zur DB zu schicken.
Als Beispiel:

Code: Alles auswählen

-- --------------------------------------------------------
-- Host:                         127.0.0.1
-- Server Version:               10.1.13-MariaDB - mariadb.org binary distribution
-- --------------------------------------------------------
 
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */
/*!40101 SET NAMES utf8mb4 */
 
-- Exportiere Datenbank Struktur für tanzdb
CREATE DATABASE IF NOT EXISTS `einedb` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `einedb`;
 
/* Mehrzeiliger Kommentar
   und auch Statements über mehrere zeilen*/

CREATE TABLE IF NOT EXISTS `kunden` (
  `KDID` int(11) NOT NULL AUTO_INCREMENT,
  `Geschlecht` varchar(255) NOT NULL DEFAULT '',
  PRIMARY KEY (`KDID`),
  UNIQUE KEY `KDID` (`KDID`),
  KEY `Nachname` (`Nachname`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;


Gibt es dafür in Lazarus eine elegante Methode zum zerteilen?

In der Unit Strutils habe ich ExtraxtWord gefunden, aber das funktioniert nur mit Character als Trennzeichen.
Irgend eine Methode in SQLDB die das schon leistet?
Bevor ich das Rad neu erfinde frage ich mal nach mit der leisen Hoffnung dass Lazarus das schon kann ?

Thx
Zuletzt geändert von charlytango am Sa 4. Mai 2019, 11:26, insgesamt 2-mal geändert.

sstvmaster
Beiträge: 345
Registriert: Sa 22. Okt 2016, 23:12
OS, Lazarus, FPC: OS: Windows 10 | Lazarus: 2.0.8 + Fixes + Trunk 32bit
CPU-Target: 32Bit
Wohnort: Dresden

Re: Datei mit SQL Statements zerteilen.

Beitrag von sstvmaster »

Als erstes könntest du versuchen die Kommentare aus dem Dump zu entfernen. Das sollte beim export mit "--skip-comments" gehen.

Dann sollte die Ausgabe so in etwa sein:

Code: Alles auswählen

CREATE DATABASE IF NOT EXISTS `einedb`;
USE `einedb`;
 
CREATE TABLE IF NOT EXISTS `kunden` (
  `KDID` int(11) NOT NULL AUTO_INCREMENT,
  `Geschlecht` varchar(255) NOT NULL DEFAULT '',
  PRIMARY KEY (`KDID`),
  UNIQUE KEY `KDID` (`KDID`),
  KEY `Nachname` (`Nachname`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

Das würde schon einiges an Arbeit ersparen.

Jetzt könnte man die Zeilenumbrüchen entfernen und dann anhand der Semikolon die einzelnen Befehle trennen.
LG Maik

charlytango
Beiträge: 199
Registriert: Sa 12. Sep 2015, 12:10
OS, Lazarus, FPC: Laz 2.0 fixes FPC 3.2 fixes
CPU-Target: Win 32Bit, 64bit
Wohnort: Wien

Re: Datei mit SQL Statements zerteilen.

Beitrag von charlytango »

sstvmaster hat geschrieben:Als erstes könntest du versuchen die Kommentare aus dem Dump zu entfernen.


Stimmt, dann wärs ja easy.

Leider bin ich nicht Herr über die SQL-Datei und so wie ich meine Pappenheimer kenne werden da immer wieder Kommentare unterschiedlichen Formates drin stehen.

Und für eigene Zwecke würde ich auch gerne (mehrzeilige) Kommentare verwenden
Aber danke für den ersten Tip

sstvmaster
Beiträge: 345
Registriert: Sa 22. Okt 2016, 23:12
OS, Lazarus, FPC: OS: Windows 10 | Lazarus: 2.0.8 + Fixes + Trunk 32bit
CPU-Target: 32Bit
Wohnort: Dresden

Re: Datei mit SQL Statements zerteilen.

Beitrag von sstvmaster »

Hmm, ok dann könntest du das nämlich so machen:

Code: Alles auswählen

procedure TForm1.FormCreate(Sender: TObject);
const
  s = 'CREATE DATABASE IF NOT EXISTS `einedb`; USE `einedb`;'+
      'CREATE TABLE IF NOT EXISTS `kunden` ('+
      '`KDID` int(11) NOT NULL AUTO_INCREMENT,'+
      '  `Geschlecht` varchar(255) NOT NULL DEFAULT '','+
      'PRIMARY KEY (`KDID`),'+
      'UNIQUE KEY `KDID` (`KDID`),'+
      'KEY `Nachname` (`Nachname`)'+
      ') ENGINE=MyISAM DEFAULT CHARSET=utf8;';
var
  sr: TStringArray;
  i: integer;
begin
  sr := s.Split(';');
  for i:=0 to Length(sr) - 1 do begin
    Memo1.Lines.Add(sr[i]);
  end;
end;


siehe Anhang.
Dateianhänge
SplitSqlDump.zip
(126.11 KiB) 37-mal heruntergeladen
Zuletzt geändert von sstvmaster am Fr 3. Mai 2019, 11:10, insgesamt 1-mal geändert.
LG Maik

sstvmaster
Beiträge: 345
Registriert: Sa 22. Okt 2016, 23:12
OS, Lazarus, FPC: OS: Windows 10 | Lazarus: 2.0.8 + Fixes + Trunk 32bit
CPU-Target: 32Bit
Wohnort: Dresden

Re: Datei mit SQL Statements zerteilen.

Beitrag von sstvmaster »

Nachtrag:

Eigentlich kommen die Kommentare direkt aus dem SQL Dump. Die gibt keiner ein!
LG Maik

charlytango
Beiträge: 199
Registriert: Sa 12. Sep 2015, 12:10
OS, Lazarus, FPC: Laz 2.0 fixes FPC 3.2 fixes
CPU-Target: Win 32Bit, 64bit
Wohnort: Wien

Re: Datei mit SQL Statements zerteilen.

Beitrag von charlytango »

sstvmaster hat geschrieben:Nachtrag: Eigentlich kommen die Kommentare direkt aus dem SQL Dump. Die gibt keiner ein!


Ich weiß, aber diese Statements sind oft auch manuell geschrieben, zur manipulation von Daten oder Strukturen uvam.
Da ist meistens (und sinnvollerweise) auch mit Kommentaren versehen

charlytango
Beiträge: 199
Registriert: Sa 12. Sep 2015, 12:10
OS, Lazarus, FPC: Laz 2.0 fixes FPC 3.2 fixes
CPU-Target: Win 32Bit, 64bit
Wohnort: Wien

Re: Datei mit SQL Statements zerteilen.

Beitrag von charlytango »

sstvmaster hat geschrieben:sr := s.Split(';');


Danke für die Mühe. Einen String anhand einem Zeichen zu zerlegen ist jetzt keine Herausforderung.
Trotzdem fand ich deine Methode schon elegant.

Die eigentliche Frage ist, wie bekomme ich die Kommentare unterschiedlichen Formats raus?

sstvmaster
Beiträge: 345
Registriert: Sa 22. Okt 2016, 23:12
OS, Lazarus, FPC: OS: Windows 10 | Lazarus: 2.0.8 + Fixes + Trunk 32bit
CPU-Target: 32Bit
Wohnort: Dresden

Re: Datei mit SQL Statements zerteilen.

Beitrag von sstvmaster »

Hab mal noch ein wenig probiert mit RegEx, das mit deinem Beispiel klappt zu 99%. [EDIT]: Jetzt geändert zum 100%?

Code: Alles auswählen

 
CREATE DATABASE IF NOT EXISTS `einedb` ;
USE `einedb`;
 
 
CREATE TABLE IF NOT EXISTS `kunden` (
  `KDID` int(11) NOT NULL AUTO_INCREMENT,
  `Geschlecht` varchar(255) NOT NULL DEFAULT '',
  PRIMARY KEY (`KDID`),
  UNIQUE KEY `KDID` (`KDID`),
  KEY `Nachname` (`Nachname`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
 
Hier jetzt nur noch die Zeielenumbrüche entfernen, dann sollte das hinkommen.



Der Code:

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
var
  r: TRegExpr;
  sl: TStringList;
  expr: string;
begin
  Memo1.Clear;
 
  sl := TStringList.Create;
  sl.LoadFromFile('sqldump.sql');
 
//  (--(.*?)\r?\n)               Kommentar -- ...
//  |                            oder verknüpfung
//  (/\*(.*?)\*/)                Kommentar /* ... */
  expr := '(--(.*?)\r?\n)|(/\*(.*?)\*/)';
 
  r := TRegExpr.Create(expr);
  Memo1.Lines.Add(trim(StringReplace(r.Replace(sl.Text,'',false),#13#10,'',[rfReplaceAll])));
  Memo1.Lines.Add('');
 
  r.Free;
  sl.Free;
 
end;
 


Rest siehe Anhang.
Dateianhänge
RegExRemoveComment2.zip
(126.92 KiB) 40-mal heruntergeladen
Zuletzt geändert von sstvmaster am Fr 3. Mai 2019, 18:43, insgesamt 2-mal geändert.
LG Maik

wp_xyz
Beiträge: 3308
Registriert: Fr 8. Apr 2011, 09:01

Re: Datei mit SQL Statements zerteilen.

Beitrag von wp_xyz »

RegEx war für mich immer ein Buch mit sieben Siegeln. Da schreibe ich mir lieber meinen eigenen Scanner. Der folgende Code nimmt an, dass
- eine SQL-Anweisung über mehrere Zeilen gehen kann und mit einem Strichpunkt endet.
- ein Kommentar mit '/*' und '*/* umschlossen ist, und
- ein einzeiliger Kommentar mit '--' eingeleitet wird.

Damit extrahiert die folgende Routine jede SQL-Anweisung und schreibt sie als einzelne Zeile in eine StringList:

Code: Alles auswählen

const
  LE = LineEnding;  // oder: #13 oder #10
  SQL =
    '-- --------------------------------------------------------' + LE +
    '-- Host:                         127.0.0.1' + LE +
    '-- Server Version:               10.1.13-MariaDB - mariadb.org binary distribution'+ LE +
    '-- --------------------------------------------------------' + LE +
    '' + LE +
    '/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */' + LE +
    '/*!40101 SET NAMES utf8mb4 */' + LE +
    '' + LE +
    '-- Exportiere Datenbank Struktur für tanzdb' + LE +
    'CREATE DATABASE IF NOT EXISTS `einedb` /*!40100 DEFAULT CHARACTER SET utf8 */;' + LE +
    'USE `einedb`;' + LE +
    '' + LE +
    '/* Mehrzeiliger Kommentar' + LE +
    '   und auch Statements über mehrere zeilen*/' + LE +
    'CREATE TABLE IF NOT EXISTS `kunden` (' + LE +
    '  `KDID` int(11) NOT NULL AUTO_INCREMENT,' + LE +
    '  `Geschlecht` varchar(255) NOT NULL DEFAULT '',' + LE +
    '  PRIMARY KEY (`KDID`),' + LE +
    '  UNIQUE KEY `KDID` (`KDID`),' + LE +
    '  KEY `Nachname` (`Nachname`)' + LE +
    ') ENGINE=MyISAM DEFAULT CHARSET=utf8;';
 
procedure ExtractSQL(SQL: String; Cmds: TStrings);
var
  P, Q: PChar;
  PEnd: PChar;
  s: String;
  n: Integer;
begin
  SetLength(s, Length(SQL));
  n := 0;
  P := @SQL[1];
  PEnd := P + Length(SQL);
  while (P < PEnd) do begin
    Q := P+1;
    if (P^ = '-') and (Q^ = '-') then begin
      // Einzeiliger Kommentar: bis zum Ende der Zeile ignorieren
      while (P < PEnd) do begin
        if (P^ = #13) and (Q^ = #10) then begin
          inc(P);
          break;
        end
        else
        if (P^ = #13) or (P^ = #10) then
          break;
        inc(P);
        inc(Q);
      end;
    end else
    if (P^ = '/') and (Q^ = '*') then begin
      // "normaler" Kommentar
      while (P < PEnd) do begin
        if (P^ = '*') and (Q^ = '/') then begin
          inc(P);
          break;
        end;
        inc(P);
        inc(Q);
      end;
    end else
    if (P^ = #13) and (Q^ = #10) then begin
      // Zeilenumbruch #13#10
      inc(P);
      inc(Q, 2);
      if n > 0 then begin
        inc(n);
        s[n] := ' ';
      end;
      // Einrückung überlesen
      while (Q < PEnd) and (Q^ = ' ') do begin
        inc(P);
        inc(Q);
      end;
    end else
    if (P^ = #13) or (P^ = #10) then begin
      // Zeilenumbruch #13 oder #10
      inc(Q);
      if n > 0 then begin
        inc(n);
        s[n] := ' ';
      end;
      // Einrückung überlesen
      while (Q < PEnd) and (Q^ = ' ') do begin
        inc(P);
        inc(Q);
      end;
    end else
    if P^ = ';' then begin
      // Ende der Anweisung
      inc(n);
      s[n] := ';';
      SetLength(s, n);
      Cmds.Add(s);
      SetLength(s, Length(SQL));
      n := 0;
    end else begin
      // Zeichen der Anweisung sammeln
      inc(n);
      s[n] := P^;
    end;
    inc(P);
  end;
  if n > 0 then begin
    SetLength(s, n);
    Cmds.Add(s);
  end;
end;
 
{ TForm1 }
 
procedure TForm1.FormCreate(Sender: TObject);
begin
  Memo1.Lines.Text := SQL;
  ExtractSQL(SQL, Memo2.Lines);
end;
 

sstvmaster
Beiträge: 345
Registriert: Sa 22. Okt 2016, 23:12
OS, Lazarus, FPC: OS: Windows 10 | Lazarus: 2.0.8 + Fixes + Trunk 32bit
CPU-Target: 32Bit
Wohnort: Dresden

Re: Datei mit SQL Statements zerteilen.

Beitrag von sstvmaster »

So habe die Lösung gefunden, der richtige RegEx wäre dieser:

Code: Alles auswählen

expr := '(--(.*?)\r?\n)|(/\*(.*?)\*/)';


Im Code oben ändere ich das noch ab.

Ja wp, RegEx ist nicht leicht, aber wenn ich sehe wie viel code du geschrieben hast im Vergleich zum Regex.
Dateianhänge
RegExRemoveComment2.zip
(126.92 KiB) 37-mal heruntergeladen
LG Maik

Socke
Lazarusforum e. V.
Beiträge: 2779
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Datei mit SQL Statements zerteilen.

Beitrag von Socke »

In FCL-DB ist das bereits enthalten: TSQLScript.NextStatement
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

sstvmaster
Beiträge: 345
Registriert: Sa 22. Okt 2016, 23:12
OS, Lazarus, FPC: OS: Windows 10 | Lazarus: 2.0.8 + Fixes + Trunk 32bit
CPU-Target: 32Bit
Wohnort: Dresden

Re: Datei mit SQL Statements zerteilen.

Beitrag von sstvmaster »

Danke socke, das geht natürlich auch. Somit kann man den SQL dump direkt verwenden.
LG Maik

charlytango
Beiträge: 199
Registriert: Sa 12. Sep 2015, 12:10
OS, Lazarus, FPC: Laz 2.0 fixes FPC 3.2 fixes
CPU-Target: Win 32Bit, 64bit
Wohnort: Wien

Re: Datei mit SQL Statements zerteilen.

Beitrag von charlytango »

WOW.
Erstmal Danke an alle Poster.
Das ist "Lazarusforum At It's Best".

Meine Erwartungshaltung war etwas elegantes in der Art von TSQLScript.NextStatement (dank an Socke) vorgeschlagen zu bekommen. Was letztlich auch geklappt hat.
Der Beitrag von sstvmaster hat mich geflasht, danke für die Mühe !
Und last not least der tolle Beitrag von wp_xyz.

Dem wollte ich nicht nachstehen und hab mir alle drei Varianten angesehen und zwei davon auch mit realen Dateien getestet.

Die "elegante" Variante fiel aus, denn die konnte ich nicht direkt übernehmen und zudem wäre ich dann von den Komponenten abhängig. Ich nutze auch ZEOS.

Bei der RegEx Variante geht es mir wie wp_xyz. Für mich immer noch ein Buch mit 7 Siegeln. Aber wenn man schon den Suchsting mitgeliefert bekommt ein Test wert. Ganz hat diese Variante nicht entsprochen weil sie nicht ganz sauber arbeitet und sicher noch Optimierung im regex-string nötig ist, die ich nicht kann.

Beim Geschwindigkeitstest mit einer 1,5 MB großen SQL Datei ergab, dass der Code von wp_xyz mit einem Faktor von 1:10 vorne lag. Also mit etwa 30ms gegen die Regex Variante mit etwa 280-340ms.
Also Spezialisierung gegen großen Funktionsumfang.

Danke nochmal, Case Closed

sstvmaster
Beiträge: 345
Registriert: Sa 22. Okt 2016, 23:12
OS, Lazarus, FPC: OS: Windows 10 | Lazarus: 2.0.8 + Fixes + Trunk 32bit
CPU-Target: 32Bit
Wohnort: Dresden

Re: Datei mit SQL Statements zerteilen.

Beitrag von sstvmaster »

Hi, schön das wir dir helfen konnten.

charlytango hat geschrieben:Beim Geschwindigkeitstest mit einer 1,5 MB großen SQL Datei ergab, dass der Code von wp_xyz mit einem Faktor von 1:10 vorne lag. Also mit etwa 30ms gegen die Regex Variante mit etwa 280-340ms.


Das ist bei mir genau anders herum :roll:

Code: Alles auswählen

 
Datei 1: 29.089.792 Bytes, MySQL
 
wp_xyz:
Load into SL: 250 ms
procedure: 109980 ms
 
sstvmaster:
Load into SL: 249 ms
RegEx: 48470 ms
 
 
Datei 2: 651.264 Bytes, MySQL
 
wp_xyz:
Load into SL: 15 ms
procedure: 1732 ms
 
sstvmaster:
Load into SL: 15 ms
RegEx: 1170 ms
 


Aber nun gut, wie dem auch sei. Hauptsache es gibt eine Lösung.
LG Maik

wp_xyz
Beiträge: 3308
Registriert: Fr 8. Apr 2011, 09:01

Re: [Gelöst] Datei mit SQL Statements zerteilen.

Beitrag von wp_xyz »

Da mein Code nur ein paar Zeichen-Vergleiche macht und den String 1x durch Pointer-Incrementierung durchläuft, kann ich mir eigentlich nicht vorstellen, dass RegEx schneller ist, zumal dein Code einen zweiten Durchlauf wegen des Split(';') erfordert (was aber vielleicht auch in den RegEx-Ausdruck eingearbeitet werden kann). Könntest du deinen Test-Code und die Test-Datei zur Verfügung stellen?

Antworten