AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Datenbanken Delphi TDBXError: Reader-hat-keine-weiteren-Zeilen
Thema durchsuchen
Ansicht
Themen-Optionen

TDBXError: Reader-hat-keine-weiteren-Zeilen

Ein Thema von Delbor · begonnen am 5. Jan 2013 · letzter Beitrag vom 5. Jan 2013
Antwort Antwort
Delbor

Registriert seit: 8. Okt 2006
Ort: St.Gallen/Schweiz
1.186 Beiträge
 
Delphi 11 Alexandria
 
#1

TDBXError: Reader-hat-keine-weiteren-Zeilen

  Alt 5. Jan 2013, 12:58
Datenbank: MySQL • Version: 5.xx • Zugriff über: DBExpress
Hi zusammen

Um es gleich vorwegzunehmen: dies ist ein Doppelpost.

Es geht darum, Bilder in eine Datenbank einzufügen; nebst den absoluten Rohdaten im Nef-Format sollen aus diesen Thumbnails im jpeg-Format in eine Bildtabelle gespeichert werden. Das für sich klappt soweit. Auch das Abholen des letzten Autoinc-Wertes nach der Einfüge-Operation war erstmal erfolgreich, und so konnten auch verknüpfte Tabellen ihre Werte erhalten.
Aber dann gings los. Was genau der Grund, bzw. die bevorstehende Änderung war, kann ich leider nicht mehr nachvollziehen. Auf jeden Fall löschte ich mal alle Testdaten, die sich in der DB angesammelt hatten und vollzog die Änderungen. Und seither meint Delphi jedes mal, wenn das Ergebnis der Abfrage 'SELECT LAST_ID' in eine Variable geschrieben werden soll, der Reader hätte keine weiteren Zeilen...

Ein Codeschnipsel aus der zuständigen Prozedur, nachdem ich von der Abfrage der Serverfunktion 'SELECT_LAST_ID' testweise auf ein nach dem höchsten Wert suchenden SQL-Statement umgestiegen war:
Delphi-Quellcode:
      ...
      SQLString := 'Insert Into Bildtabelle(Thumbnail, NEF) Values (:FThumbMemory,:FNefMemory)';
      DelborDataMod.SQLDataSetXE.CommandType := (ctQuery);
      DelborDataMod.SQLDataSetXE.CommandText := SQLString;
      DelborDataMod.SQLDataSetXE.Params.CreateParam(ftBlob, 'FThumbMemory', ptInput);
      DelborDataMod.SQLDataSetXE.Params.CreateParam(ftBlob, 'FNefMemory', ptInput);
      DelborDataMod.SQLDataSetXE.Params[0].LoadFromStream(FThumbMemory, ftgraphic);
      DelborDataMod.SQLDataSetXE.Params[1].LoadFromStream(FNefMemory, ftgraphic);
      DelborDataMod.SQLDataSetXE.ExecSQL(false);
      DelborDataMod.SQLDataSetXE.Close;
    SelectLastId(SQLString);
      Progressbar1.Position := Progressbar1.Position+1;
      Application.ProcessMessages;
    FReportList.Add(' '+ IntToStr(i)+'-TPicAdmin.BildTabelleInsert');
    FReportList.Add(' ');
      BildDescribeTabelleInsert(BildTabelleLastId,BildName);
    end;
  finally
  ...
Und SelectLastID:

Delphi-Quellcode:
procedure TPicAdmin.SelectLastId(SQLString: string);
begin
  SQLString := 'Select MAX(idBild) from Bildtabelle';
  // SQLString := 'Select Last_Insert_ID()';
  // DelborDataMod.SQLDataSetXE.GetMetadata := true;
  // DelborDataMod.SQLDataSetXE.SetSchemaInfo(stIndexes,'Bildtabelle','nil',''); //
  DelborDataMod.SQLDataSetXE.CommandType := (ctQuery);
  DelborDataMod.SQLDataSetXE.CommandText := SQLString;
  DelborDataMod.SQLDataSetXE.Open;
  DelborDataMod.SQLDataSetXE.First;
  while not DelborDataMod.SQLDataSetXE.Eof do
  begin
    LbxLastID.Items.Add(IntToStr(DelborDataMod.SQLDataSetXE.Fields[0].AsInteger));
  end;
end;
Aufgerufen wird diese Prozedur hier:

Delphi-Quellcode:
procedure TPicAdmin.Alle_oeffnenClick(Sender: TObject);
  var Seitenlaenge: Integer; AUser, APass: String;
      Transaction : TDBXTransaction;
begin
  OpenDlg.Filetype := '.NEF';
   APass := CM_Main.ProjektFrame1.MaskEdit1.Text;
  AUser := CM_Main.ProjektFrame1.Edi_User.Text;
  Seitenlaenge := 100;
  if OpenDlg.ShowModal = mrOK then begin
    if DelborDataMod.GetSQLConnection(AUser,APass) then begin
      DelborDataMod.SQLDataSetXE.SQLConnection := DelborDataMod.SQLConnectionXE;
      Transaction := DelborDataMod.SQLConnectionXE.BeginTransaction;
      DelborDataMod.SQLConnectionXE.KeepConnection := true;
      DelborDataMod.SQLConnectionXE.AutoClone := true;
      LbxLastId.Items.Add('ActiveStatements := ' + IntToStr(DelborDataMod.SQLConnectionXE.ActiveStatements));
      LbxLastId.Items.Add('MaxStmtsPerConn := ' + IntToStr(DelborDataMod.SQLConnectionXE.MaxStmtsPerConn));
      // MaxStmtsPerConn- Wert := 1 / Readonly
      DelborDataMod.SQLDataSetXE.Prepared := false;
    FReportList.Add(' - TPicAdmin.Alle_oeffnenClick');
      BildTabelleInsert2(Seitenlaenge);
    FReportList.Add(' - TPicAdmin.Alle_oeffnenClick');
    WriteReportFile; //<== erstellt eine Reportdatei zur Ausgabe von Variablenwerte etc.
      DelborDataMod.SQLConnectionXE.CommitFreeAndNil(Transaction);
    end;
  end;
end;
Für Interessierte habe ich die komplette Bildinsert-Prozedur als Pdf angehängt; ich finde, sie ist etwas zu monströs, um sie hier in voller Länge zu posten.

Ich gehe mal davon aus, dass die Variable 'MaxStmtsPerConn' ihren Wert vom MySQL-Server erhält und habe da nach einer entsprechenden Variablen gesucht. Bisher allerdings erfolglos.

Ich hab mich auch schon in Embarcaderos Seiten zum DBX-Framework vertieft, alllerdings bis jetzt mit eher unbefriedigendem Ergebnis - entweder steht da nichts über das was ich suche, oder ich habs überlesen...

Was mache ich falsch?

Für eure Antworten schon mal vielen Dank!

Gruss
Delbor
Angehängte Dateien
Dateityp: pdf procedure TPicAdmin2.pdf (8,7 KB, 2x aufgerufen)
Roger
Man muss und kann nicht alles wissen - man muss nur wissen, wo es steht.
Frei nach Albert Einstein
http://roase.ch
  Mit Zitat antworten Zitat
Benutzerbild von sx2008
sx2008

Registriert seit: 15. Feb 2008
Ort: Baden-Württemberg
2.332 Beiträge
 
Delphi 2007 Professional
 
#2

AW: TDBXError: Reader-hat-keine-weiteren-Zeilen

  Alt 5. Jan 2013, 13:24
Ich sehe da mehrere Probleme in deinem Code.

1.) du hast ein Datenmodul (DelborDataMod) und darauf ein Dataset (SQLDataSetXE).
Es macht wenig Sinn stets das gleiche Dataset für unterschiedliche SQL-Anweisungen zu benützen.
Stattdessen sollte man mehrere Datasets benützen und möglichst alle Eigenschaften (insbesondere CommandText) im Objektinspektor einstellen.
Kurz: für jeden Zweck ein eigenes Dataset

2.) du verwendest ExecSQL und Close hintereinander.
Entweder eine SQL-Anweisung gibt ein Dataset zurück (kann eigentlich nur ein SELECT sein)
oder eben nicht (z.B. INSERT, UPDATE oder DELETE).
Wenn man ein Dataset erwartet verwendet man Open und Close.
Falls man kein Dataset erwartet verwendet man ExecSQL.
Diese beiden Fälle sollen nicht vermischt werden

3.) bestimme Abfragen wie z.B. Select MAX(idBild) from Bildtabelle können grundsätzlich nur einen Datensatz liefern.
Daher sollte der Code so aussehen:

Delphi-Quellcode:
procedure TPicAdmin.SelectLastId(SQLString: string);
var
  s : string;
begin
  (* im Objektinspektor festgelegt, braucht daher nicht im Code zu sein
  soll hier nur dem Verständnis dienen

  SQLString := 'Select MAX(idBild) as MaxID from Bildtabelle';
  DelborDataMod.SQLDataSetMaxId.CommandType := (ctQuery);
  DelborDataMod.SQLDataSetMaxId.CommandText := SQLString;
  *)


  DelborDataMod.SQLDataSetMaxId.Close; // zur Sicherheit erst mal schliesen
  DelborDataMod.SQLDataSetMaxId.Open;
  s := IntToStr(DelborDataMod.SQLDataSetMaxId.Fields[0].AsInteger);
  DelborDataMod.SQLDataSetMaxId.Close;

  // nur hinzufügen, falls noch nicht in der Liste
  // andernsfalls könnte die gleiche ID mehrfach eingefügt werden
  if LbxLastID.Items.IndexOf(s) <> -1 then
    LbxLastID.Items.Add(s);
end;
4.) achte darauf, dass Felder immer einen definierten Namen bekommen.
Bei der Abfrage Select MAX(idBild) from Bildtabelle hängt es von der Datenbank ab, welchen Namen das Feld bekommt.
Daher sollte man den Namen mit der AS-Klausel vorgeben:
Select MAX(idBild) AS LastId from Bildtabelle

Geändert von sx2008 ( 5. Jan 2013 um 13:28 Uhr)
  Mit Zitat antworten Zitat
Delbor

Registriert seit: 8. Okt 2006
Ort: St.Gallen/Schweiz
1.186 Beiträge
 
Delphi 11 Alexandria
 
#3

AW: TDBXError: Reader-hat-keine-weiteren-Zeilen

  Alt 5. Jan 2013, 14:13
Hi sx2008

Grundsätzlich denke ich, hast du damit recht, verschiedene DataSets zu verwenden. Ich sehe allerdings nicht,weshalb die Funktion eingeschränkt werden soll, wenn ich das einemal eine Selectanweisung und das andere mal ein Insert-Statement über dasselbe Dataset ausführe.

Zitat:
(* im Objektinspektor festgelegt, braucht daher nicht im Code zu sein
soll hier nur dem Verständnis dienen
Hmm... Das heisst dann: für jede Abfrage ein eigenes Dataset? Ist das nicht etwas viel?
Zum Beispiel, wenn eine Abfrage die ersten drei Felder liefern soll, eine andere alle und eine dritte nur eines?

Zitat:
du verwendest ExecSQL und Close hintereinander.
Wenn nach dem ExecSQL der Aufruf der Prozedur SelectLastId erfolgt und da das Dataset nach deinem Vorschlag erstmal geschlossen wird, entspricht dies in der Praxis genau dem... Sorry.
Das Close steht da auch erst, seit ich mit 'SELECT..MAX' arbeite und sicher sein wollte, das das Dataset auch wirklich geschlossen ist. Ich gehe mal davon aus, dass der Reader, von dem hier die Rede ist, die Objektinstanz ist, die das DataSet ausliest und von diesem erzeugt wird, wenn die Ergebnisse eines SELECT angefordert werden.

Zitat:
...achte darauf, dass Felder immer einen definierten Namen bekommen.
ÄÄÄhhh - und dann das einzige angeforderte und gelieferte Feld über seinen Index abfrage?

Gruss
Delbor
Roger
Man muss und kann nicht alles wissen - man muss nur wissen, wo es steht.
Frei nach Albert Einstein
http://roase.ch
  Mit Zitat antworten Zitat
Benutzerbild von sx2008
sx2008

Registriert seit: 15. Feb 2008
Ort: Baden-Württemberg
2.332 Beiträge
 
Delphi 2007 Professional
 
#4

AW: TDBXError: Reader-hat-keine-weiteren-Zeilen

  Alt 5. Jan 2013, 14:45
Grundsätzlich denke ich, hast du damit recht, verschiedene DataSets zu verwenden. Ich sehe allerdings nicht,weshalb die Funktion eingeschränkt werden soll, wenn ich das einemal eine Selectanweisung und das andere mal ein Insert-Statement über dasselbe Dataset ausführe.
Wenn du nur ein Dataset verwendest ist es im Prinzip wie ein globales Objekt und kann damit unerwünschte Seiteneffekte verursachen.

Das heisst dann: für jede Abfrage ein eigenes Dataset? Ist das nicht etwas viel?
Zum Beispiel, wenn eine Abfrage die ersten drei Felder liefern soll, eine andere alle und eine dritte nur eines?
Ja, für jeden Zweck ein eigenes Dataset. Um das zu organiseren gibt es Datenmodule.
Die meisten Abfragen sind fest, d.h. man kann sie schon im Objektinspektor festlegen.
Dynamische Abfragen, also wenn das SqlCommand zur Laufzeit zusammengebaut wird, kann man natürlich auch verwenden, aber das ist eher selten.
Man kann ca 20 bis 30 Datasets auf ein Datenmodul packen bevor es unübersichtlich wird.
ÄÄÄhhh - und dann das einzige angeforderte und gelieferte Feld über seinen Index abfrage?
In dem Fall sollte man das Feld über den Namen ansprechen.

Ich verstehe übrigens nicht so ganz, was du mit der LastId eigentlich bezweckst.
Eigentlich muss man die höchste ID nur abfragen, wenn man einen neuen Datensatz einfügen möchte.
Dann braucht man aber die höchste ID plus 1.
Und die Insertanweisung wäre dann Insert Into Bildtabelle(idBild, Thumbnail, NEF) Values (:FidBild, :FThumbMemory,:FNefMemory) weil man die neue ID schon beim Einfügen mitgeben muss.

Falls das Feld idBild ein Autoincrement-Feld ist muss man es allerdings beim Insert weglassen.
Dann ergibt sich allerdings das Problem, dass man nicht weiss mit welcher ID der letzte Datensatz eingefügt wurde. (was passiert wenn mehrere Clients quasi gleichzeitig einfügen?)
Ich würde empfehlen auf Autoincrement-Felder komplett zu verzichten und stattdessen die neue ID direkt vor dem Insert zu ermitteln.
  Mit Zitat antworten Zitat
Delbor

Registriert seit: 8. Okt 2006
Ort: St.Gallen/Schweiz
1.186 Beiträge
 
Delphi 11 Alexandria
 
#5

AW: TDBXError: Reader-hat-keine-weiteren-Zeilen

  Alt 5. Jan 2013, 18:32
Hi sx2008

Ich verstehe übrigens nicht so ganz, was du mit der LastId eigentlich bezweckst.
Die DB besteht aus insgesamt 7 Tabellen, die alle untereinander verknüpft sind; 2 davon mit n:m-Beziehungen, der Rest ist 1:n. Den letzten eingefügten/erzeugten Wert brauche ich für den Fremdschlüssel der Detailtabelle.

Falls das Feld idBild ein Autoincrement-Feld ist muss man es allerdings beim Insert weglassen.
Dann ergibt sich allerdings das Problem, dass man nicht weiss mit welcher ID der letzte Datensatz eingefügt wurde. (was passiert wenn mehrere Clients quasi gleichzeitig einfügen?)
Die Primärschlüssel sämtlicher Tabellen sind autoinc-Felder. MySQL kennt die Funtion 'SELECT_LAST_INSERT_ID', die den letzten erzeugten Autoinc-Wert zurückliefert(InnoDB). Das hat ja erstmal auch funktioniert - so, wie es mir jetzt erscheint, glaub ich mich zu erinnern, dass ich erstmal diese Prozedur aufbaute, bevor ich die nächsten Insert-Prozeduren schrieb. Und seither krieg ich die bewusste Fehlermeldung...
Die Funktion 'SELECT_LAST_INSERT_ID' ist übrigens Verbindungsbezogen und mindestens der in Bearbeitung stehende Datensatz gesperrt. MySQL 5.1.xx ist da recht überzeugend.

Ich würde empfehlen auf Autoincrement-Felder komplett zu verzichten und stattdessen die neue ID direkt vor dem Insert zu ermitteln.
Das hab ich mir auch schon überlegt, hatte dann aber den Eindruck, dass das DBX-Framework von DelphiXE inzwischen ein recht starkes Werkzeug ist. Fundierte Kenntnissse des Frameworks würden mir wohl bei der Entwicklung weiterer DB-bezogener Programme gute Dienste leisten, su dass ich vieles nicht mehr 'barfuss machen' muss.

Ich denk, ich werd' mal ein Testprogrämmchen zum Austesten des Frameworks schreiben und hier vorerst auf 'Indexvergabe per Hand' umsteigen.

Gleichwohl interessiert es mich immer noch, wenn irgendwer Kenntnisse über das DBX-Framework hat.

Gruss
Delbor
Roger
Man muss und kann nicht alles wissen - man muss nur wissen, wo es steht.
Frei nach Albert Einstein
http://roase.ch
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 02:29 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz