Query.Delete Zeiger auf nächsten Datensatz [erledigt]

Für Themen zu Datenbanken und Zugriff auf diese. Auch für Datenbankkomponenten.
Antworten
Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Query.Delete Zeiger auf nächsten Datensatz [erledigt]

Beitrag von Michl »

Hallo,

da ich eine definitive Aussage nicht ergoogeln konnte, frage ich mal hier.

Ist es definitiv immer gewährleistet, daß bei einem Query.Delete der Zeiger auf den nächsten Datensatz gesetzt wird? Bei Zeos und SQLDB funktioniert das hier, aber ist dies dauerhaft gewährleistet bzw. definiert?

Hintergrund ist, daß ich alle Datensätze einer Tabelle auslese, in den Zwischenspeicher lade, auswerte und nicht mehr benötigte lösche. Ungefähr so:

Code: Alles auswählen

  Query.First;
  while not Query.EOF do
  begin
    if not NeededAnymore(Query.Fields[1].AsInteger) then
      Query.Delete
    else
      Query.Next;
  end;  
Testbeispiel wo alle Zahlen außer Primzahlen gelöscht werden anbei (ohne DLL)
Dateianhänge
SqlitePrimeZeos.zip
(2.6 KiB) 50-mal heruntergeladen
SqlitePrimeSQLDB.zip
(2.63 KiB) 50-mal heruntergeladen
Zuletzt geändert von Michl am Mo 14. Mär 2022, 16:14, insgesamt 1-mal geändert.

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

Sieben
Beiträge: 202
Registriert: Mo 24. Aug 2020, 14:16
OS, Lazarus, FPC: Ubuntu Xenial 32, Lazarus 2.2.0, FPC 3.2.2
CPU-Target: i386

Re: Query.Delete Zeiger auf nächsten Datensatz

Beitrag von Sieben »

Ich denke, dass diese Verhaltensweise recht stabil ist, uq weil eine Änderung auch gravierende Folgen für das User-Interface hätten. Dürfte, wenn überhaupt, nicht 'über Nacht' eingeführt werden.

Aber mal anders gefragt - die Kriterien für NeededAnymore lassen sich nicht in SQL formulieren?

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: Query.Delete Zeiger auf nächsten Datensatz

Beitrag von Michl »

Sieben hat geschrieben:
Mo 14. Mär 2022, 14:01
Aber mal anders gefragt - die Kriterien für NeededAnymore lassen sich nicht in SQL formulieren?
Nein, ich muss die Tabelle einzeln durchgehen und jeden Eintrag mit recht komplexen Berechnungen prüfen. Ich könnte da natürlich die IDs rausschreiben und hinterher die Einträge mit den jeweiligen IDs löschen, aber das kann ich ja auch gleich machen.
Sieben hat geschrieben:
Mo 14. Mär 2022, 14:01
Ich denke, dass diese Verhaltensweise recht stabil ist, uq weil eine Änderung auch gravierende Folgen für das User-Interface hätten. Dürfte, wenn überhaupt, nicht 'über Nacht' eingeführt werden.
Ich habe leider in der Vergangenheit schon manchmal Federn lassen müssen, weil ich an eine bestimmte Verhaltensweise geglaubt habe :wink: - daher die Frage.

Ganz konsequent ist o.g. Verhaltensweise nicht, da wenn der letzte Datensatz gelöscht wird, Query nicht auf EOF springt, sondern den vorherigen Datensatz auswählt (in den Beispielen wird z.B. 97 zweimal getestet). D.h. ich habe die Schleife aktuell in etwa so gebaut (vielleicht gibt es da eine bessere Möglichkeit, als RecNo?!):

Code: Alles auswählen

procedure TForm1.ButtonRemoveClick(Sender: TObject);
var
  LNo: LongInt;
begin
  Query.First;
  while not Query.EOF do
  begin
    if not IsPrime(Query.Fields[1].AsInteger) then
    begin
      LNo := Query.RecNo;
      Query.Delete;
      if Query.RecNo < LNo then
        Query.Next;
    end
    else
      Query.Next;
  end;
end;  

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

Sieben
Beiträge: 202
Registriert: Mo 24. Aug 2020, 14:16
OS, Lazarus, FPC: Ubuntu Xenial 32, Lazarus 2.2.0, FPC 3.2.2
CPU-Target: i386

Re: Query.Delete Zeiger auf nächsten Datensatz

Beitrag von Sieben »

Du könntest es ohne RecNo zB so machen:

Code: Alles auswählen

procedure TForm1.ButtonRemoveClick(Sender: TObject);
begin
  Query.First;
  while not Query.EOF do
  begin
    if not IsPrime(Query.Fields[1].AsInteger) then
    begin
      LNo := Query.RecNo;
      Query.Delete;
      Query.Next;
      if Query.EOF then
        Break
      else
        Query.Prior;
    end
    else
      Query.Next;
  end;
end;  
Aber einmal zweimal testen ist jetzt auch nicht sooo schlimm, oder?

Benutzeravatar
gladio
Beiträge: 217
Registriert: Sa 21. Jun 2014, 06:15
OS, Lazarus, FPC: Win10-64 - aktuelle Lazarus/FPC Standard-Edition
CPU-Target: 64Bit
Wohnort: Rügen

Re: Query.Delete Zeiger auf nächsten Datensatz

Beitrag von gladio »

Die Datensätze werden nach deinem ersten Codefragment zuverlässig nacheinander abgearbeitet.
ich habe das selbst schon mehrmals so genutzt, ohne daß Datensätze übrig geblieben sind oder übersprungen wurden (Zeos, SQLite oder Firebird).
Bei großen Datenmengen könnte es etwas zeitkritisch werden.

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: Query.Delete Zeiger auf nächsten Datensatz

Beitrag von Michl »

Sieben hat geschrieben:
Mo 14. Mär 2022, 15:50
Du könntest es ohne RecNo zB so machen:
Das stimmt, das geht auch. Habe es jetzt so umgesetzt. Danke.
Sieben hat geschrieben:
Mo 14. Mär 2022, 15:50
Aber einmal zweimal testen ist jetzt auch nicht sooo schlimm, oder?
Bei dem Beispiel nicht, doch bei meiner echten Anwendung hat das die Folge, daß sich nach der Auswertung Grundlagendaten geändert haben und dies dann zu fehlerhaften Daten führt.
Zuletzt geändert von Michl am Mo 14. Mär 2022, 16:21, insgesamt 1-mal geändert.

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: Query.Delete Zeiger auf nächsten Datensatz

Beitrag von Michl »

gladio hat geschrieben:
Mo 14. Mär 2022, 16:02
Die Datensätze werden nach deinem ersten Codefragment zuverlässig nacheinander abgearbeitet.
ich habe das selbst schon mehrmals so genutzt, ohne daß Datensätze übrig geblieben sind oder übersprungen wurden (Zeos, SQLite oder Firebird).
Bei großen Datenmengen könnte es etwas zeitkritisch werden.
Danke für die Info. Dann nehme ich das mal so als gesetzt hin und betrachte das erstmal als erledigt.

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6209
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: Query.Delete Zeiger auf nächsten Datensatz [erledigt]

Beitrag von af0815 »

Vom SQLdb oder ZEOS wird im Hintergrund ein Puffer geführt, in dem die Änderungen mitprotokolliert werden. Deswegen ist das relativ stabil. Probleme bekommt man, wenn in einer Multiuserumgebung arbeitet, dann kann sich ein Problem ergeben, wenn beim Einpflegen in die DB festgestellt wird, das im Hintergrund sich was geändert hat. Meist fällt es auf, wenn was gelöscht wird.

Da muss man im Design darauf Rücksicht nehmen und wenn nötig das mit Transaktionen absichern.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

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

Re: Query.Delete Zeiger auf nächsten Datensatz [erledigt]

Beitrag von wp_xyz »

Michl hat geschrieben:
Mo 14. Mär 2022, 11:05
Ist es definitiv immer gewährleistet, daß bei einem Query.Delete der Zeiger auf den nächsten Datensatz gesetzt wird? Bei Zeos und SQLDB funktioniert das hier, aber ist dies dauerhaft gewährleistet bzw. definiert?
Zunächst hätte ich gesagt, dass das so ist, also dass nach einem Dataset.Delete der nächste Record aktiv wird. Und Michael Van Canneyt schreibt im "Lazarus Handbook" auch: "The current record will then be deleted, and the cursor will be positioned on the next record in the Dataset (if there is one)"

Aber wenn man sich den Quellcode von TDataset ansieht, ist das keinesfalls garantiert. Zunächst mal soll ein Abkömmling von TDataset die Methode InternalDelete überschreiben, und dort den Datensatz löschen. Das wird von Delete mit allem Drum-Herum aufgerufen. Das wäre OK.

Aber Delete ist als virtual deklariert - das heißt, ich könnte in meinem eigenen TDataset-Abkömmling ein komplett anderes Verhalten implementieren und den Cursor nach dem Löschen an den Anfang setzen (das wäre gemein...), oder den Dataset auf Active=false setzen, damit z.B. zur Sicherheit keine weiteren Datensätze gelöscht werden können, oder warum und was auch immer. Logisches Verhalten bei Fremdkomponenten darf man nicht immer voraussetzen.

Michl
Beiträge: 2505
Registriert: Di 19. Jun 2012, 12:54

Re: Query.Delete Zeiger auf nächsten Datensatz [erledigt]

Beitrag von Michl »

Danke af0815 und wp!
wp_xyz hat geschrieben:
Mo 14. Mär 2022, 18:12
Und Michael Van Canneyt schreibt im "Lazarus Handbook" auch: "The current record will then be deleted, and the cursor will be positioned on the next record in the Dataset (if there is one)"
Danke, daß du das rausgesucht hast! Dann ist das gesetzt, zumindest, wenn man es nicht selber verbockt, wie du es ausgeführt hast.

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection; 

Antworten