Datenbankverbindung
-
- Beiträge: 143
- Registriert: Mo 30. Mär 2020, 12:37
Datenbankverbindung
[gelöst] Hallo, ich habe als Datenbankneuling mal eine Frage an die Experten:
Gibt es eine Regel, wie man Datenbankverbindungen herstellt und Abfragen ausführen muß? Ich habe häufig das Problem, daß ich die Verbindung (MySQL) über eine Procedure herstelle, mittels "FMLogIn.SQLQuery1.SQL.Text:=..." eine Abfrage formuliere und mit "FMLogIn.SQLQuery1.execSQL" losschicke. Zwischen der Verbindungserstellung und der Ausführung liegen mitunter noch einige andere Befehle oder eine Abfrage wird in einer Schleife gemacht mit der Folge, daß ich keine Ergebnisse oder Fehlermeldungen bekomme (z.B., daß keine Felder gefunden worden sind (die es natürlich gibt!)). Falls ich mit zwei Tabellen arbeite werden ebenfalls keine Ergebnisse geliefert.
Muß man also nach jeder Verbindung, der Abfrage und der Ausführung die Verbindung immer wieder schließen und z.B. für die zweite Tabelle wieder eine Verbindung herstellen? Falls in einer Schleife Abfragen erfolgen muß man also immer wieder in der Schleife die Verbindung herstellen, die Abfrage formulieren und ausführen und dann die Verbindung wieder schließen??(hoffentlich konnte ich mich verständlich ausdrücken......)
Vielen Dank für die Hilfe
Gibt es eine Regel, wie man Datenbankverbindungen herstellt und Abfragen ausführen muß? Ich habe häufig das Problem, daß ich die Verbindung (MySQL) über eine Procedure herstelle, mittels "FMLogIn.SQLQuery1.SQL.Text:=..." eine Abfrage formuliere und mit "FMLogIn.SQLQuery1.execSQL" losschicke. Zwischen der Verbindungserstellung und der Ausführung liegen mitunter noch einige andere Befehle oder eine Abfrage wird in einer Schleife gemacht mit der Folge, daß ich keine Ergebnisse oder Fehlermeldungen bekomme (z.B., daß keine Felder gefunden worden sind (die es natürlich gibt!)). Falls ich mit zwei Tabellen arbeite werden ebenfalls keine Ergebnisse geliefert.
Muß man also nach jeder Verbindung, der Abfrage und der Ausführung die Verbindung immer wieder schließen und z.B. für die zweite Tabelle wieder eine Verbindung herstellen? Falls in einer Schleife Abfragen erfolgen muß man also immer wieder in der Schleife die Verbindung herstellen, die Abfrage formulieren und ausführen und dann die Verbindung wieder schließen??(hoffentlich konnte ich mich verständlich ausdrücken......)
Vielen Dank für die Hilfe
Zuletzt geändert von Joachim Raap am Fr 15. Mai 2020, 17:20, insgesamt 3-mal geändert.
- willi4willi
- Lazarusforum e. V.
- Beiträge: 170
- Registriert: Sa 1. Nov 2008, 18:06
- OS, Lazarus, FPC: Lazarus 3.8 FPC 3.2.2 x86_64-win64-win32/win64 x86_64-linux-gtk2
- CPU-Target: i386, win64, arm
Re: Datenbankverbindung
Hallo Joachim,
nein, die Verbindung musst du nicht schließen.
Du baust einmal die Verbindung auf, schickst über diese Verbindung mehrere SQL-Abfragen oder Befehle an die Datenbank und schließt diese anschließend wieder, wenn du sie nicht mehr benötigst.
Falls sich die Verbindung hin und wieder von allein verabschiedet, also trennt, dann prüfe man deine Timeout - Einstellungen bei der Datenbank. Ich hatte da auch hin und wieder Probleme.
Wenn du auf Datenbankabfragen keine Ergenbisse erhältst, ohne Fehlermeldung, dann ist vielleicht die Verbindung noch vorhanden, aber die Abfrage hat ein Problem.
Das Problem ist derart, dass die Syntax der Abfrage richtig ist. Daher keine Fehlermeldung.
Dabei gehe ich einmal davon aus, dass du die Fehlermeldungen nicht abfängst (try -- except).
Mein Tipp:
nein, die Verbindung musst du nicht schließen.
Du baust einmal die Verbindung auf, schickst über diese Verbindung mehrere SQL-Abfragen oder Befehle an die Datenbank und schließt diese anschließend wieder, wenn du sie nicht mehr benötigst.
Falls sich die Verbindung hin und wieder von allein verabschiedet, also trennt, dann prüfe man deine Timeout - Einstellungen bei der Datenbank. Ich hatte da auch hin und wieder Probleme.
Wenn du auf Datenbankabfragen keine Ergenbisse erhältst, ohne Fehlermeldung, dann ist vielleicht die Verbindung noch vorhanden, aber die Abfrage hat ein Problem.
Das Problem ist derart, dass die Syntax der Abfrage richtig ist. Daher keine Fehlermeldung.
Dabei gehe ich einmal davon aus, dass du die Fehlermeldungen nicht abfängst (try -- except).
Mein Tipp:
- Prüfe vor einer Abfrge, ob die Verbindung noch steht ( if Connection.connected then ...),
- Prüfe deine Abfragen mal außerhalb deines Codes, indem du sie irgendwie ausgibst (z.B. Writeln(FMLogIn.SQLQuery1.SQL.Text); readln; ) und über einen Mysql-Client ausführst.
Viele Grüße
Willi4Willi
------------
-
- Beiträge: 143
- Registriert: Mo 30. Mär 2020, 12:37
Re: Datenbankverbindung
Hallo,
mh - ich habe mal die Procedure hierherkopiert
Wenn ich das so mache, dann gibt es den Fehler "Field not found - Sta_ArtNr"
Aufgabe der Procedure ist in der Datenbank "nachzusehen", ob es einen Satz mit der Artikelnummer gibt. Wenn ja, ist alles gut, wenn nein, dann soll es die Artikelnummer ist eine Fehlertabelle übernehmen die hinterher an den User zur Behebung ausgegeben wird.
Wo/wie kann ich den "Timeout" prüfen?
mh - ich habe mal die Procedure hierherkopiert
Code: Alles auswählen
procedure TFMCSV.BtPruefenClick(Sender: TObject);
begin
FMLogIn.ConnectDB;
for i := 0 to FMCSV.LVCSV.Items.Count -1 do
begin
s:=FMCSV.LVCSV.Items.Item[i].Subitems[1]; //SubItems[1] enthält die Artikelnummer - Übergabe an s
FMLogIn.SQLQuery1.SQL.Text:='select * from art_stamm where Sta_ArtLoesch=0 and Sta_ArtNr='''+s+''';';
FMLogIn.SQLQuery1.execSQL;
strStaNr:=FMLogIn.SQLQuery1.FieldByName('Sta_ArtNr').AsString;
if strStaNr='' then
begin
FMLogIn.SQLQuery1.SQL.Text:='insert into Fehler (Nr) values ('''+s+''');';
FMLogIn.SQLQuery1.execSQL;
end;
end;
FMLogIn.CloseDB;
end;
Aufgabe der Procedure ist in der Datenbank "nachzusehen", ob es einen Satz mit der Artikelnummer gibt. Wenn ja, ist alles gut, wenn nein, dann soll es die Artikelnummer ist eine Fehlertabelle übernehmen die hinterher an den User zur Behebung ausgegeben wird.
Wo/wie kann ich den "Timeout" prüfen?
-
- Beiträge: 143
- Registriert: Mo 30. Mär 2020, 12:37
Re: Datenbankverbindung
Nachtrag
ich habe die Procedure wie folgt umgeschrieben:
.....und alles ist gut! Vielen Dank
ich habe die Procedure wie folgt umgeschrieben:
Code: Alles auswählen
procedure TFMCSV.BtPruefenClick(Sender: TObject);
begin
FMLogIn.ConnectDB;
for i := 0 to FMCSV.LVCSV.Items.Count -1 do
begin
s:=FMCSV.LVCSV.Items.Item[i].Subitems[1]; //SubItem[1] enthält die Artikelnummer
FMLogIn.SQLQuery1.SQL.Text:='select * from art_stamm where Sta_ArtLoesch=0 and Sta_ArtNr='''+s+''';';
try
begin
FMLogIn.SQLQuery1.execSQL;
strStaNr:=FMLogIn.SQLQuery1.FieldByName('Sta_ArtNr').AsString;
end;
except
begin
if strStaNr='' then
FMLogIn.SQLQuery1.SQL.Text:='insert into Fehler (Nr) values ('''+s+''');';
FMLogIn.SQLQuery1.execSQL;
end;
end;
end;
FMLogIn.CloseDB;
end;
- af0815
- Lazarusforum e. V.
- Beiträge: 6774
- Registriert: So 7. Jan 2007, 10:20
- OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
- CPU-Target: 32Bit (64Bit)
- Wohnort: Burgenland
- Kontaktdaten:
Re: Datenbankverbindung
Generell: Versuch kein gefrickel mit den Strings, sondern verwende dafür Parameter. Irgendwann landest du sonst in der Hochkommahölle 
Ausserdem ist einer der Bausteine zum Schutz gegen SQL-Injections. Die passieren auch mal wenn wer 'or true heisst

Ausserdem ist einer der Bausteine zum Schutz gegen SQL-Injections. Die passieren auch mal wenn wer 'or true heisst

Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).
- willi4willi
- Lazarusforum e. V.
- Beiträge: 170
- Registriert: Sa 1. Nov 2008, 18:06
- OS, Lazarus, FPC: Lazarus 3.8 FPC 3.2.2 x86_64-win64-win32/win64 x86_64-linux-gtk2
- CPU-Target: i386, win64, arm
Re: Datenbankverbindung
Das funktioniert tatsächlich so?
Ich hätte nach
ein
erwartet.
Für das Verhalten der mysql/MariaDB-Datenbank gibt es verschiede Variablen. Die kann man in der Datenbank auch global setzen.
Wenn du den Zugriff darauf nicht hast, dann kannst du das auch in deinem Programm nach der Verbindungsaufnahme für die Session setzen.
Ich mache immer sowas:
Ich hätte nach
Code: Alles auswählen
FMLogIn.SQLQuery1.SQL.Text:='select * from art_stamm where Sta_ArtLoesch=0 and Sta_ArtNr='''+s+''';';
Code: Alles auswählen
FMLogIn.SQLQuery1.Open;
if not FMLogIn.SQLQuery1.EOF()
then strStaNr:=FMLogIn.SQLQuery1.FieldByName('Sta_ArtNr').AsString
else .....
Für das Verhalten der mysql/MariaDB-Datenbank gibt es verschiede Variablen. Die kann man in der Datenbank auch global setzen.
Wenn du den Zugriff darauf nicht hast, dann kannst du das auch in deinem Programm nach der Verbindungsaufnahme für die Session setzen.
Ich mache immer sowas:
Code: Alles auswählen
Connect;
ExecuteDirect('SET SESSION wait_timeout = 31536000;');
ExecuteDirect('SET SESSION net_read_timeout = 1000;');
Viele Grüße
Willi4Willi
------------
-
- Beiträge: 143
- Registriert: Mo 30. Mär 2020, 12:37
Re: Datenbankverbindung
Ich noch mal....
Ja, funktioniert grundsätzlich schon. Ich meine jedoch in meinem Unwissen, daß der Vorschlag (den mit ....SQLQuery1.EOF() ) nicht verwendbar ist weil ich mir nicht alle Datensätze nacheinander vornehme sondern gezielt einen, der der entsprechenden ArtNr entspricht (den gibt es auch nur einmal).....
Dennoch gibt es jetzt einen inhaltlichen Fehler: In die Fehlertabelle werden alle Sätze geschrieben auch die, die es gibt. Das muß irgendwie an dem Befehl "strStaNr:=FMLogIn.SQLQuery1.FieldByName('Sta_ArtNr').AsString;" liegen - die variable bleibt nämlich immer leer.
Kannst Du da einen Fehler entdecken warum der Wert von Sta_ArtNr nicht übertragen wird????? (heul)
Ja, funktioniert grundsätzlich schon. Ich meine jedoch in meinem Unwissen, daß der Vorschlag (den mit ....SQLQuery1.EOF() ) nicht verwendbar ist weil ich mir nicht alle Datensätze nacheinander vornehme sondern gezielt einen, der der entsprechenden ArtNr entspricht (den gibt es auch nur einmal).....

Dennoch gibt es jetzt einen inhaltlichen Fehler: In die Fehlertabelle werden alle Sätze geschrieben auch die, die es gibt. Das muß irgendwie an dem Befehl "strStaNr:=FMLogIn.SQLQuery1.FieldByName('Sta_ArtNr').AsString;" liegen - die variable bleibt nämlich immer leer.
Kannst Du da einen Fehler entdecken warum der Wert von Sta_ArtNr nicht übertragen wird????? (heul)
-
- Beiträge: 143
- Registriert: Mo 30. Mär 2020, 12:37
Re: Datenbankverbindung
schön af0815 - dann werde doch mal konkret: Was für Parameter?
-
- Beiträge: 143
- Registriert: Mo 30. Mär 2020, 12:37
Re: Datenbankverbindung
willi4willi, ich habe die procedure Deinem Vorschlag entsprechend noch mal geändert; sie sieht jetzt so aus:
Leider aber wird der Wert aus der Datenbank (in Sta_ArtNr) immer noch nicht an die Variable strStaNr übergeben so das strStaNr immer leer bleibt. Kannst Du einen Fehler sehen?
Code: Alles auswählen
procedure TFMCSV.BtPruefenClick(Sender: TObject);
begin
FMLogIn.ConnectDB;
for i := 0 to FMCSV.LVCSV.Items.Count -1 do
begin
s:=FMCSV.LVCSV.Items.Item[i].Subitems[1]; //SubItem[1] enthält die Artikelnummer -> an s übergeben
FMLogIn.SQLQuery1.SQL.Text:='select * from art_stamm where Sta_ArtLoesch=0 and Sta_ArtNr='''+s+''';';
FMLogIn.SQLQuery1.open;
if not FMLogIn.SQLQuery1.EOF then strStaNr:=FMLogIn.SQLQuery1.FieldByName('Sta_ArtNr').AsString;
if strStaNr='' then
begin
FMLogIn.SQLQuery1.SQL.Text:='insert into Fehler (Nr) values ('''+s+''');';
FMLogIn.SQLQuery1.execSQL;
end;
end;
FMLogIn.CloseDB;
end;
- willi4willi
- Lazarusforum e. V.
- Beiträge: 170
- Registriert: Sa 1. Nov 2008, 18:06
- OS, Lazarus, FPC: Lazarus 3.8 FPC 3.2.2 x86_64-win64-win32/win64 x86_64-linux-gtk2
- CPU-Target: i386, win64, arm
Re: Datenbankverbindung
Das sollte so funktionieren, ohne zu wissen, wie die Datenbank-Verbindung aufgebaut wird.
Hast du einmal mit einem MySQL-Client getestet, ob deine Abfrage auch wirklich einen Wert zurückgibt?
Ansonsten fallen mir noch folgende Dinge auf:
Übrigens, der Tipp von af0815 lohnt in deinem Fall wirklich nachzugehen. In dem Fall bedarf es nur ein FMLogIn.SQLQuery1.refresh;, wenn die Artikelnummer als Parameter gesetzt ist.
Den SQL.Text definierst du dann nur einmal. Den Insert würde ich dann direkt über die Connection senden.
Wie gesagt es lohn sich, sich mit den Datenbankgrundlagen näher zu befassen. Ich habe da auch lange "herumgestochert", weil es einfach zu viel Durcheinander gibt.
Wegen der Parameter: Schau mal hier!
Hast du einmal mit einem MySQL-Client getestet, ob deine Abfrage auch wirklich einen Wert zurückgibt?
Ansonsten fallen mir noch folgende Dinge auf:
- Die Variable strStaNr sollte initialisiert bzw. zurückgesetzt sein.
- Enthält SubItem[1], die an s übergeben wird, wirklich die nur Artikelnummer? Oder sind da evtl. noch Leerzeichen mit im Spiel?
Code: Alles auswählen
procedure TFMCSV.BtPruefenClick(Sender: TObject);
begin
FMLogIn.ConnectDB;
for i := 0 to FMCSV.LVCSV.Items.Count -1 do
begin
s:=FMCSV.LVCSV.Items.Item[i].Subitems[1]; //SubItem[1] enthält die Artikelnummer -> an s übergeben
s:=trim(s); //< verhindern, dass da noch unerwünschte Leerzeichen sind
//< falls die aber in der Datenbank tatsächlich vorhanden sind, dann
//< darfst du das natürlich nicht machen
FMLogIn.SQLQuery1.SQL.Text:='select * from art_stamm where Sta_ArtLoesch=0 and Sta_ArtNr='''+s+''';';
FMLogIn.SQLQuery1.open;
strStaNr:=''; //< Initialisieren bzw. zurücksetzen, sonst steht ja noch der letzte Wert drin
if not FMLogIn.SQLQuery1.EOF then strStaNr:=FMLogIn.SQLQuery1.FieldByName('Sta_ArtNr').AsString;
FMLogIn.SQLQuery1.close; //< Dataset wieder schließen,
if strStaNr='' then
begin
FMLogIn.SQLQuery1.SQL.Text:='insert into Fehler (Nr) values ('''+s+''');';
FMLogIn.SQLQuery1.execSQL;
end;
end;
FMLogIn.CloseDB;
end;
Den SQL.Text definierst du dann nur einmal. Den Insert würde ich dann direkt über die Connection senden.
Wie gesagt es lohn sich, sich mit den Datenbankgrundlagen näher zu befassen. Ich habe da auch lange "herumgestochert", weil es einfach zu viel Durcheinander gibt.
Wegen der Parameter: Schau mal hier!
Viele Grüße
Willi4Willi
------------
-
- Beiträge: 143
- Registriert: Mo 30. Mär 2020, 12:37
Re: Datenbankverbindung
willi4willi hat geschrieben: Fr 15. Mai 2020, 14:41
Hast du einmal mit einem MySQL-Client getestet, ob deine Abfrage auch wirklich einen Wert zurückgibt?
[/code]
Hallo,
ja, es wird ein Wert zurückgegeben (ich habe dann die Abfrage auch direkt nach Lazarus kopiert...)
-
- Beiträge: 143
- Registriert: Mo 30. Mär 2020, 12:37
Re: Datenbankverbindung
mh - Deine Version funktioniert!
Ich verstehe nicht wirklich, warum noch in die Tabelle "Fehler" geschrieben wird, weil doch die Verbindung vorher geschlossen worden ist - und dann zu Schluß noch einmal...
Es scheint wohl aber ein Unterschied zu geben - SQLQuery1.close ist wohl was anderes als CloseDB.........
Ansonsten hast Du recht - ich muß noch viel über DB lernen; bislang habe ich meine Kenntnisse durch "abgucken" gewonnen (reicht wohl nicht)
DANKE
Ich verstehe nicht wirklich, warum noch in die Tabelle "Fehler" geschrieben wird, weil doch die Verbindung vorher geschlossen worden ist - und dann zu Schluß noch einmal...
Es scheint wohl aber ein Unterschied zu geben - SQLQuery1.close ist wohl was anderes als CloseDB.........
Ansonsten hast Du recht - ich muß noch viel über DB lernen; bislang habe ich meine Kenntnisse durch "abgucken" gewonnen (reicht wohl nicht)
DANKE
- af0815
- Lazarusforum e. V.
- Beiträge: 6774
- Registriert: So 7. Jan 2007, 10:20
- OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
- CPU-Target: 32Bit (64Bit)
- Wohnort: Burgenland
- Kontaktdaten:
Re: Datenbankverbindung
Schaut bei mir meist so aus
für so Abfragen verwende ich oft eine temporär erzeugte Query, die oft nur eine View am Server abfragt. Weil die View, wenn komplexer bereits am Server vorkompiliert gespeichert werden kann.
Übrigends ist der Unterschied zwischen ExecSQL und Open der, das bei ExecSQL normalerweise kein Resultset zurückerwartet wird, sondern nur ein Ergebnis. Wird normalerweise nur dann verwendet, wenn die Abfrage nur eine Zeile mit einem Wert beinhaltet oder für Update, Insert und Delete, da hier kein Resultset zurückkommt, sondern maximal ein Result (affected lines).
Faustregel, wenn Select, dann ist ein Open zwingend, bei Update, Insert und Delete ist es ExecSQL.
Übrigend sagt nach einen Open die Abfrage "Query.BOF and Query.EOF" ob es ein Resultset gibt. Das sollte man Abfragen bevor man die Felder des Resultsets abfragt. Man erspart sich Ärger damit bei der Abfrage.
Code: Alles auswählen
...
SQL := ''+LineEnding
+' SELECT Top 1 [DateTime],'+ LineEnding
+' [Fehlergruppe], [Fehler]'+ LineEnding
+' FROM [dbo].[mytable]' + LineEnding
+' WHERE TRIM(Produkt) like TRIM(:Param1)' + LineEnding
+' ORDER BY [DateTime] DESC';
...
Query.Active := false;
Query.SQL.Clear;
Query.SQL.Add(SQL);
Query.Params.CreateParam(ftString,'Param1',ptInput);
Query.Params.ParamByName('Param1').Value := aParam;
Query.Active := True;
Query.First;
....
Übrigends ist der Unterschied zwischen ExecSQL und Open der, das bei ExecSQL normalerweise kein Resultset zurückerwartet wird, sondern nur ein Ergebnis. Wird normalerweise nur dann verwendet, wenn die Abfrage nur eine Zeile mit einem Wert beinhaltet oder für Update, Insert und Delete, da hier kein Resultset zurückkommt, sondern maximal ein Result (affected lines).
Faustregel, wenn Select, dann ist ein Open zwingend, bei Update, Insert und Delete ist es ExecSQL.
Übrigend sagt nach einen Open die Abfrage "Query.BOF and Query.EOF" ob es ein Resultset gibt. Das sollte man Abfragen bevor man die Felder des Resultsets abfragt. Man erspart sich Ärger damit bei der Abfrage.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).
-
- Beiträge: 143
- Registriert: Mo 30. Mär 2020, 12:37
Re: Datenbankverbindung
danke af0815.
Aber das verstehe ich vielleicht in einem halben Jahr........
Wie gesagt, ich bin Anfänger (wie Du weißt)!
Aber das verstehe ich vielleicht in einem halben Jahr........
Wie gesagt, ich bin Anfänger (wie Du weißt)!