Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Datenbanken (https://www.delphipraxis.net/15-datenbanken/)
-   -   TAdoQuery Row cannot be located .. bei post (https://www.delphipraxis.net/193961-tadoquery-row-cannot-located-bei-post.html)

norwegen60 29. Sep 2017 01:31

Datenbank: Mssql • Version: 2008 • Zugriff über: TAdo

TAdoQuery Row cannot be located .. bei post
 
Hallo,

ich habe ein sehr seltsames Phänomen bei einem übernommen Programm. Der Programmierer hat dort folgendes Konstrukt programmiert:

Delphi-Quellcode:
  Table1.Append;
  Table1['Field1'] := 'Wert1';
  Table1['Field2'] := 'Wert2';
  Table1['Field3'] := 'Wert3';
  Table1['Field4'] := 'Wert4';
 
  try
    Table1.Post;
  except
  end
  Table1.Edit;

  Table1['Field5'] := 'Wert5';
  Table1['Field6'] := 'Wert6';

  try
    Table1.Post;
  except
  end
  Table1.Edit;
  usw....
Beim zweiten Post kommt immer die Exception: Row cannot be located for updating. Some values may have been changed since it was last read
Oder auf Deutsch: die zum aktualisieren angegebene zeile wurde nicht gefunden: einige werte wurden seit dem letzten lesen geändert

Warum der Programmierer zwischen drin immer wieder Post/Edit aufruft muss ich noch ermitteln. Trotzdem möchte ich wissen, was diese Fehlermeldung hervorrufen kann. Table1 ist mit keinem Ereignis oder Datasource verbunden und es greift sicher auch kein anderer Prozess auf den Datensatz zu.
Ich habe folgendes versucht
  • Ursprünglich war Table1 ein TAdoTable. Eine Änderung auf TAdoQuery brachte keine Abhilfe.
  • Auch die mir genehmere Langschreibweise
    Delphi-Quellcode:
    Table1.FieldByName('Field5').AsString
    brachte nichts
  • Sobald ich
    Delphi-Quellcode:
    Table1['Field5'] := 'Wert5';
    vor das erste Post schiebe, funktioniert alles
  • Field5 ist ein einfaches nvarchar(30)-Feld wie die anderen auch
  • Das Problem kann mit Datenbanken unterschiedlicher Installationen nachvollzogen werden, tritt aber immer mit demselben Feld auf
  • Das Feld hat keinerlei Indizes, Einschränkungen oder sonstiges

Hat jemand eine Idee woran das liegen kann

Danke
Gerd

Aviator 29. Sep 2017 01:37

AW: TAdoQuery Row cannot be located .. bei post
 
Ist das wirklich der komplette Auszug? Oder fehlt da noch was zwischen dem
Delphi-Quellcode:
Table1.Edit
und dem
Delphi-Quellcode:
Table1['Field5'] := 'Wert5';
? Weil so sollte das eigentlich funktionieren.

Auf der anderen Seite könntest du auch sagen, dass du seinen SourceCode optimierst und dann auch die Felder gleich vor dem ersten Post (be)schreibst. Macht ja nur Sinn und beschleunigt das Programm auch etwas. Ich würde solche Stellen direkt überarbeiten wenn ich mir sicher bin, dass das Programm danach genau gleich oder sogar besser/effizienter arbeitet.

norwegen60 29. Sep 2017 01:50

AW: TAdoQuery Row cannot be located .. bei post
 
Zitat:

Zitat von Aviator (Beitrag 1382223)
Ist das wirklich der komplette Auszug? Oder fehlt da noch was zwischen dem
Delphi-Quellcode:
Table1.Edit
und dem
Delphi-Quellcode:
Table1['Field5'] := 'Wert5';
? Weil so sollte das eigentlich funktionieren.

Auf der anderen Seite könntest du auch sagen, dass du seinen SourceCode optimierst und dann auch die Felder gleich vor dem ersten Post (be)schreibst. Macht ja nur Sinn und beschleunigt das Programm auch etwas. Ich würde solche Stellen direkt überarbeiten wenn ich mir sicher bin, dass das Programm danach genau gleich oder sogar besser/effizienter arbeitet.

Ja, das ist alles. Im Except wird noch in ein Log geschrieben, aber sonst ist es alles. Was fehlt denn noch? Der wollte einfach immer wieder Zwischenspeichern (Auch wenn es gar kein Sinn macht, wenn der Datensatz unvollständig ist)

Durch das Optimieren bin ich da ja drauf gestoßen. Aber die Zuweisungen ziehen sich über 1000 Zeilen, also nicht mal so auf dei Schnelle optimiert, auch wenn eine Überarbeitung dringenst nötig wäre. Der Fehler tritt aber schon im zweiten post-Edit Block auf.

jobo 29. Sep 2017 07:55

AW: TAdoQuery Row cannot be located .. bei post
 
Wie sieht es denn mit einem Primary Key auf der Tabelle aus.

TigerLilly 29. Sep 2017 08:46

AW: TAdoQuery Row cannot be located .. bei post
 
Check mal, ob es ev einen Filter gibt, der dafür sorgt, dass nach dem Post ein anderer Datensatz den Fokus hat.
Kontrolliere mal Indices, ob es da welche gibt, die die Sichtbarkeit beeinflussen.

Sind die Zuweisungen wirklich "Wert1" etc?

Die Table ist ja an eine Datenbank gebunden. Welche ist das denn? Kannst du anzeigen lassen, was direkt auf der Datenbank passiert, mit einem Trace oder Profiler?

Bei den Felddefinitionen gibt es die Einstellung, wie die Aktualisierung in der DB durchgeführt werden soll + welche Felder berücksichtigt werden sollen: KeyOnly, WhereChangedOnly etc. Schau mal, ob sich die Felder da unterscheiden.

nahpets 29. Sep 2017 09:53

AW: TAdoQuery Row cannot be located .. bei post
 
Der Fehler ist typisch, wenn man die Ergebnismenge eines SQLs wie eine Tabelle verändert und dabei ein Schlüsselwert geändert wird.
Ebenfalls tritt der Fehler auf, wenn durch eine Änderung mehrere Sätze betroffen sind und man dann später einen bereits geänderten Satz ein weiteres mal ändern will.

Z. B. sowas:
SQL-Code:
select * from tabelle where field1 = :wert1
Wird nun Field1 ein neuer Wert zugewiesen, und ist nicht (zwingend) eindeutig, so kann die Datenbank nicht mehr sicher feststellen, welcher Datensatz geändert werden soll. Dies führt (u. a.) zur vorliegenden Fehlermeldung.
Alternativ könnten hier bei einer Wertzuweisung auf field1 mehrere Sätze geändert werden. Möchte man nun einen so geänderten Satz ändern, ist der genannte Fehler unvermeidlich.

Um hier etwas genauer auf einen möglichen Fehler prüfen zu können, schau bitte mal, ob Table1 ein Attribut RowsAffected (o. ä.) hat. Wenn direkt nach einem Post dort ein Wert > 1 enthalten ist, wurden mehrere Sätze geändert und die Änderung eines dieser Sätze führt im späteren Verlauf zwingend zu dieser Fehlermeldung.

Das vom Programmierer gewählte Vorgehen funktioniert nur dann, wenn in der Auswahl der Daten garantiert ein eindeutiger Schlüssel vorhanden ist und der Schlüssel nicht geändert wird und sichergestellt ist, dass eine Änderung nur einen Satz betrifft.

Es handelt sich hier eher um eine sehr instabile Vorgehensweise.

Um zu schauen, ob das Konstrukt überhaupt sicher aufrecht zu erhalten ist, müsste man mal sehen, wie die Datenauswahl für Table1 aussieht und auch mal des Createstatement der zugrundeliegenden Tabelle "begutachten".

Eventuell ist es aber sinnvoller, sich von dieser Routine (ohne weitere Nachforschung) zu verabschieden und die Änderungen per SQL-Update durchzuführen.

Sofern mit der Table1 keine Datasource und keine Anzeigekomponenten verbunden sind, würd' ich mir hier keine Gedanken machen, sondern die Routine wegwerfen und die Änderungen per
SQL-Code:
Update table set field1 = :Wert1 ... where Schlüsselspalte = :Schlüsselwert
machen.

Die Chance, dass das vorhanden Konstrukt nicht dauerhaft sicher und fehlerfrei "hinzubekommen" ist, ist sehr groß.

p80286 29. Sep 2017 22:26

AW: TAdoQuery Row cannot be located .. bei post
 
Zitat:

Zitat von nahpets (Beitrag 1382235)
Die Chance, dass das vorhanden Konstrukt nicht dauerhaft sicher und fehlerfrei "hinzubekommen" ist, ist sehr groß.

Etwas weniger diplomatisch ausgedrückt, wer die Daten einer Tabelle ohne eindeutige Identifizierung der Datensätze (PrimaryKey) bearbeitet und sich auf Nebeneffekte verläßt (z.B Sortierung) der handelt verantwortungslos.

Gruß
K-H

jobo 29. Sep 2017 23:05

AW: TAdoQuery Row cannot be located .. bei post
 
Also ich weiß nicht ob es hilfreich ist, dataset Befehle und SQL Queries in den Hinweisen zu vermischen.
Ein SQL Befehl ist vollkommen transparent, er braucht auch keinen PK. Alle Datensätze, die laut Where Bedingung betroffen sind, werden aktualisiert, analog bei Delete. Da ist sogar eine Sortierung egal, sie kann nicht mal angegeben werden.

Die Frage ist doch, wie ein Append im Dataset gegen eine spezifische DB implementiert ist und wie mglw gesetzte Eigenschaften des Datasets wie bspw. Filter, Sort ... da reinspielen. Und: Da append nicht insert ist, ein anhängen aber sehr wohl was anderes als ein insert, spielt hier schon eine Reihenfolge eine Rolle und eben die Frage, ob überhaupt ein PK definiert ist.

Dann gibt es noch Verfahren, die beim Update eine where Clause aus den alten Feldwerten bauen, um zu vermeiden, dass Daten aktualisert werden, die zwischenzeitlich verändert wurden. Das würde wohl direkt zu einer solchen Fehlermeldung führen.

Um das zu prüfen, müsste man mal die Post Events durchschauen und ob da noch irgendwelche anderen Komponenten verdrahtet sind.

p80286 30. Sep 2017 10:17

AW: TAdoQuery Row cannot be located .. bei post
 
:oops:
Natürlich hast Du recht, mir ging es nur darum einen Datensatz eindeutig zu identifizieren. damit man alle Änderungen mit diesem Datensatz durchführen kann. Ein beliebter Fehler ist ja das Prinzip
Hole Datenmenge where Datum = 01.01.2001
ändere in Datenmenge Datum in 02.01.2001
Hole Datenmenge where Datum = 01.01.2001
ändere in Datenmenge Betrag in 0,01 ===> keine Datensätze verfügbar!

Gruß
K-H

nahpets 30. Sep 2017 10:59

AW: TAdoQuery Row cannot be located .. bei post
 
Was mir gerade noch einfällt:

Im Ausgangspost wird zuerst ein Append gemacht, Werte vergeben gespeichert. Dann der Satz wieder editiert und gespeichert.

Bei Append und anschließenden Post sind im Dataset in der Regel die technischen Schlüssel der Datenbank, die per Trigger, Sequence, AutoInc ... von der Datenbank vergeben werden nicht bekannt. Auch hier tritt der genannte Fehler auf.

Datenbank und Dataset können sich quasi nicht darauf einigen, welcher Datensatz zu ändern ist, da sie nicht gegenseitig über alle zur Identifizierung benötigten Informationen verfügen.

Erste Lösungsmöglichkeit wäre, die "zwischengeschobenen" Edits und Post zu entfernen.
Delphi-Quellcode:
Table1.Append;
  Table1['Field1'] := 'Wert1';
  Table1['Field2'] := 'Wert2';
  Table1['Field3'] := 'Wert3';
  Table1['Field4'] := 'Wert4';
  Table1['Field5'] := 'Wert5';
  Table1['Field6'] := 'Wert6';
  usw....
  // Erst dann, wenn alle Werte vergeben wurden.
  try
    Table1.Post;
  except
  end;
Eine Änderung des Datensatzes ist erst möglich, wenn er aus der Datenbank gelesen wurde, z. B. Table1.Refresh und Positionierung auf dem Satz.

jobo 30. Sep 2017 20:58

AW: TAdoQuery Row cannot be located .. bei post
 
Das ist mir nicht aufgefallen. Da nähern wir uns der Sache wohl!
Dem Append dürfte der PK wohl auch egal sein, solange nicht mehr als einmal editiert wird.
Aber das ist hier der Fall.
@peinlich:
Geht mir auch manchmal so, im Eifer des Gefechts.. es war ja eigentlich klar worum es Dir ging, also mir jedenfalls.

norwegen60 7. Okt 2017 09:23

AW: TAdoQuery Row cannot be located .. bei post
 
Vielen Dank für die Tips und dazu mein Feedback
  • Ja, die Tabelle hat einen Primarykey
  • Das Verhalten ändert sich nicht ob ich Insert oder Append verwende. Ich glaube nicht, dass Dataset hier überhaupt einen Unterschied macht. In beiden Fällen liefert DataSet.State = dsInsert und schau ich mir im MsSQL-Profiler an, was dort geschieht, sehe ich beide male
    Code:
    exec sp_executesql N'SET NOCOUNT OFF; INSERT INTO "TestDB".."analysis" ("Bezeichnung") VALUES (@P1); SELECT SCOPE_IDENTITY() AS SCOPE_ID_COLUMN',N'@P1 nvarchar(6)',N'Insert'
    (Diese Beispiel stimmt nicht mit der Datenstruktur oben überein, sondern war nur kurzer Test)
  • DataSet hat einen Filter. Der ist so gesetzt, dass er nur die Daten der aktuellen Workstation anzeigt. Der neue Datensatz erfüllt diese Bedingung. Ich selber verwende nie Filter sondern filtere immer schon im SQL mit where. Ich werden den Filter eliminieren, auch wenn er keine Rolle spielen sollte.
  • Ich habe alle Post - Edit entfernt. Ganz zum Schluss brauch ich dann aber doch eines, da in bestimmten Fällen, die erzeugte PK in eine Feld geschrieben wird. Das funktioniert jetzt zwar, aber da ich auch nicht sicher weiß warum die Exception auftritt, bin ich auch nicht sicher, ob ich in jedem Fall die korrekte PK zurück bekomme.
  • Es gibt keine weiteren Ereignishandler in DataSet oder DataSource. Ich suche aber noch, ob vieleleicht auf einem der Editfelder was passiert.
Ich werde den kompletten Insert in eine eigenes Dataset legen, würde aber gern bei Insert - Post bleiben. Per SQL ist mir im Moment der Aufwand zu groß um eines für Insert und eines für Update mit den XX-Feldern zu schreiben. Da diese Dataset mit keinerlei Filtern oder Ereignissen verknüpft sein wird, ist ja wohl sichergsetellt, dass der Datensatz nach dem Post derselbe ist wie der, der gepostet wurde.

Welche Möglichkeiten gäbe es denn sonst noch um sicherzustellen, dass man nach dem Post auf dem korrekten Datansatz steht? Bei den Daten, die ich selber mit
Code:
  Insert Into..
 // hängt immer ein
 select SCOPE_IDENTITY() as ID');
 // Dran um die erzeugte ID zurück zu bekommen.
Gibt es sowas bei Post auch?

Mein Problem im vorliegenden Fall: Außer der PK, die ja erst beim Post erzeugt wird, habe ich keine eindeutigen Suchkey. Wenn, dann am ehesten das CreatDate. Da bin ich mir aber nicht sicher ob das immer funktioniert. Ich hatte mit MsSQL bei Realzahlen schon das Problem, dass die Originalzahl und die in der DB gespeicherte Zahl in den letzten Bits nicht übereingestimmt haben, ein Vergleich
Code:
Where rOriginal = Feld.Value
also fehlschlug.

TigerLilly 7. Okt 2017 09:43

AW: TAdoQuery Row cannot be located .. bei post
 
Ah, dein PK wird vom Server erzeugt!
1) Schau doch mal mit dem Profiler nach, welche where Klausel beim Post erzeugt wird.
2) Schau dir mal an, welche UpdateOptions bei den Felddefinitionen gesetzt sind.

Das Insert/Post funktioniert. Dabei ändert aber der Server den PK, was ja auch so gewollt ist. Bei deinem nachfolgenden Edit/Post sind die Daten in deinem Dataset + in der DB aber deshalb nicht mehr ident. Siehe auch:
http://docwiki.embarcadero.com/Libra...oGenerateValue

Bernhard Geyer 7. Okt 2017 11:27

AW: TAdoQuery Row cannot be located .. bei post
 
Delphi-Quellcode:
  try
    Table1.Post;
  except
  end;
Kann man den Entwickler für dieses Konstrukt noch zur Rechenschaft ziehen?
Ein stilles verschlucken von Fehlermeldungen an dieser Stelle ist - sagen wir mal so - suboptimal.
Ich glaube nicht das die Anwendung vernünftig weiter arbeiten kann wenn beim Speichern in der DB ein Fehler auftritt.

nahpets 7. Okt 2017 12:42

AW: TAdoQuery Row cannot be located .. bei post
 
Suboptimal ist in diesem Zusammenhang sehr freundlich, bei uns galt das immer als Synonym für ein absolutes NoGo. Und das ist es hier in diesem Falle auch.

Was die Datenbankschnittstelle zusätzlich zum eigentlichen Insert noch so an Statements absetzt, ist eher irrelevant. Soweit ich das bisher in meiner Programmierpraxis mit ADO feststellen konnte, erfährt ein TDataSet nichts von den von der Datenbank erzeugten Schlüssel, bis man die Daten neu von der Datenbank abfragt.

Interesant wäre es mal zu wissen, wie die Einstellungen der Datenbankverbindung bzw. der DataSet-Komponente sind.

Was steht z. B. in CacheSize, CursorLocation, CursorType, ExecuteOptions, LockType, ...? (Nach Herstellung der Datenbankverbindung.)

Änderungen an diesen Eigenschaften können zu massiv unterschiedlichem Verhalten führen und haben, je nach Datenbanktyp, sehr unterschiedliche Konsequenzen auf das Laufzeitverhalten von Programmen.

Der hier vorliegende Fehler tritt immer auf, wenn in der Datenbank noch "selbstständig" Werte vergeben werden (Trigger, Sequenzen, AutoInc ...)

Ein Abfolge von Append, Post, Edit, Post ... funktioniert nur dann, wenn immer alle Inhalte der Datensätze in der Datenbank und im DataSet identisch sind (also fast nie in Client/Serverumgebungen :-()

Wenn es zwingend erforderlich ist (warum auch immer) bei Insert und Post zu bleiben, dann versuche mal, ob ein sofort auf das Post folgendes Refresh das Problem löst. Das DataSet wird dadurch gezwungen, die Daten erneut von der Datenbank abzufragen, man muss den Datensatzzeiger aber neu auf dem zu verarbeitenden Datensatz positionieren.

Allerdings:
Es wird immer die gesamte Datenmenge neu geladen, die Funktion ist stark von der Einstellung der oben genannten Eigenschaften abhängig und erhöht ggfls. die Laufzeit (datenmengenabhängig) massiv.

Zitat:

Da diese Dataset mit keinerlei Filtern oder Ereignissen verknüpft sein wird, ist ja wohl sichergsetellt, dass der Datensatz nach dem Post derselbe ist wie der, der gepostet wurde.
Darauf würde ich nicht wetten, wenn z. B. ein Index gesetzt ist, kann sich beim Post die Position im TDataSet verändern, ob der Cursor entsprechend "mitwandert" oder auf dem Satz steht, der sich nun an der (ehemaligen) Position des Satzes befindet, müsste man mal überprüfen (z. B. Eigenschaften IndexFieldNames, IndexName bei TAdoTable).
Hier hat die Eigenschaft CursorLocation ggfls. Auswirkungen. Prüf' hier bitte mal, ob eine Änderung von clUseClient nach clUseServer, bzw. umgekehrt, je nach aktuellem Zustand, zu einem veränderten Verhalten führt.

norwegen60 8. Okt 2017 18:55

AW: TAdoQuery Row cannot be located .. bei post
 
Zitat:

Zitat von Bernhard Geyer (Beitrag 1382787)
Delphi-Quellcode:
  try
    Table1.Post;
  except
  end;
Kann man den Entwickler für dieses Konstrukt noch zur Rechenschaft ziehen?
Ein stilles verschlucken von Fehlermeldungen an dieser Stelle ist - sagen wir mal so - suboptimal.
Ich glaube nicht das die Anwendung vernünftig weiter arbeiten kann wenn beim Speichern in der DB ein Fehler auftritt.

Absolut einig. Der Entwickler zeigt sich auch einsichtig. Das hilft aber nicht wirklich, da das was er in 15 Jahren programmiert hat, so ist wie es ist.
Durch das Hinterfragen der ein oder anderen Technik ist uns heute auch klar, warum es hin und wieder Fehlermeldungen aus dem Feld gibt, wo irgendwelche Daten unvollständig, oder noch schlimmer, einem anderen Datensatz zugeordnet wurden. Mittlerweile wundert es uns, dass solche Fehler nicht viel häufiger vorkommen.
Von daher ist es keine Frage, dass das komplett aufgeräumt werden muss. Nur bei all dem, was da sonst so drum rum programmiert wurde, ist das nichts was man mal in ein paar Stunden macht. Deshalb suche ich jetzt nach dem Quick&Dirty-Weg, der zumindest sicherstellt, dass ich nach dem Post auch noch auf dem richtigen Datensatz stehe. Ich kenne die TAdo nicht sehr gut da ich mit UniDac gearbeitet habe. Verlasse mich dort nach einem Post aber meist auch darauf, dass sich die Position nicht ändert.:oops:

Die Dataset-Einstellungen sind weitgehend Standard:
AutoCalcFields = true, CahceSize = 1, CommandoType = cmdText, CursorLocation = clUserClient, CursorType = ctKeyset, EnableBCD = False, IndexFieldNames = NULL, LockType = ltOptimistic, MarshalOptions = moMarshalAll, ParamCheck = False, Prepared = False, StoreDefs = False
Datenbank ist wie gesagt MsSQL 2008R2 und 2014

In diesem speziellen Fall sollten die Daten immer identisch sein, da jede Workstation nur seine Daten ändern kann. Auch wenn das eine Einschränkug ist, die eher stört.

Zitat:

Zitat von nahpets (Beitrag 1382802)
Wenn es zwingend erforderlich ist (warum auch immer) bei Insert und Post zu bleiben, dann versuche mal, ob ein sofort auf das Post folgendes Refresh das Problem löst. Das DataSet wird dadurch gezwungen, die Daten erneut von der Datenbank abzufragen, man muss den Datensatzzeiger aber neu auf dem zu verarbeitenden Datensatz positionieren.

Genau das wollte ich ja machen, suche aber noch nach optimalen Weg, wie ich sicher wieder auf den Datensatz zurück komme, den ich bearbeitet habe. Beim Insert habe ich noch keinen eindeutigen Key.

Ein Anstatz wäre, den Satz per
Zitat:

Insert Into..
select SCOPE_IDENTITY() as ID');
anzulegen. Dann hätte ich den PK und könnte den Rest weiter per Dataset und Locate... abhandeln. Wirklich toll finde ich die Lösung allerdings nicht. Aber in mind. einen sauren Apfel muss ich wohl beißen.

Auf jeden Fall haben mir eure Tips noch den ein oder anderen Gesichtspunkt genannt, an den ich noch nicht gedacht habe.

hoika 9. Okt 2017 04:32

AW: TAdoQuery Row cannot be located .. bei post
 
Hallo,
schau mal hier

https://stackoverflow.com/questions/...dodb-recordset


Alle Zeitangaben in WEZ +1. Es ist jetzt 00:22 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz