Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Datenbanken (https://www.delphipraxis.net/15-datenbanken/)
-   -   Meldung "0 record(s) updated. Only one record should have been updated" (https://www.delphipraxis.net/205444-meldung-0-record-s-updated-only-one-record-should-have-been-updated.html)

erich.wanker 10. Sep 2020 12:16

Datenbank: mySQL • Version: 5.7.29 - MySQL Community Server (GPL) • Zugriff über: Zeos 7.2.6-stable

Meldung "0 record(s) updated. Only one record should have been updated"
 
Hallo Leute,

ich bekomme Fehler 0 record(s) updated. Only one record should have been updated.

wenn ich auf mehreren Clients (mit meiner Software) auf die Mysql Datenbank am Server zugreife.

Solange ich nur eine Client-App starte gibt es keine Probleme.


Jede Tabelle hat ein Primary Field (id)


Verbinden tu ich jeden Client mit einer Zconnection

Code:
object ZConnection1: TZConnection
    ControlsCodePage = cCP_UTF16
    AutoEncodeStrings = True
    Catalog = ''
    Properties.Strings = (
      'controls_cp=CP_UTF16'
      'CLIENT_FOUND_ROWS=1'
      'AutoEncodeStrings=ON')
    TransactIsolationLevel = tiReadCommitted
    HostName = ''
    Port = 3306
    Database = 'datenbank'
    User = 'karotte'
    Password = ''
    Protocol = 'mysqld-5'
    LibraryLocation = 'C:\Server\htdocs\libmysql.dll'
    Left = 52
    Top = 28
  end

Jedes ZQuery hat "wmWhereKeyOnly" als WhereMode und "umUpdateChanged" als UpdateMode (CachedUpdates = false) ShowRecordTypes =[usUnmodified,usModified,usInserted]


Meine Typischen SQL´s schauen so aus:


Code:
  ZQuery.SQL.Clear;
  ZQuery.SQL.append('SELECT * FROM tabelle where `Dings` = "' + stringvariable + '" ');
  ZQuery.Open;

  if ZQuery.RecordCount > 0 then
    begin
      ZQuery.Edit;
      ZQuery.fieldbyname('Feld').Asinteger := 12345;
      ZQuery.Post;
    end;

Hat jemand eine Idee wieso ich den Fehler bekomme?

Danke für Hinweise

Erich

Delphi.Narium 10. Sep 2020 13:36

AW: Meldung "0 record(s) updated. Only one record should have been updated"
 
Schlüsselverletzung: (Constraint oder eindeutiger Index)

Irgendwer vergibt für ID im Primary Field (id) einen Werte, der schon in der Tabelle vorhanden ist.

Es hätten mit diesem Wert mehrere Datensätze aktuallisiert werden müssen, es dürfte aber nur ein Datensatz aktuallisiert werden, deshalb wird kein Datensatz aktuallisiert, sondern diese Fehlermeldung ausgegeben.

Nach welcher Logik wird denn die ID vergeben?

Automatisch von der Datenbank per AutoInc, Sequenz, ... oder macht das jeder Client für sich aus?

Desweiteren:
Delphi-Quellcode:
  ZQuery.SQL.Clear;
  ZQuery.SQL.append('SELECT * FROM tabelle where `Dings` = "' + stringvariable + '" ');
  ZQuery.Open;

  if ZQuery.RecordCount > 0 then
    begin
      ZQuery.Edit;
      ZQuery.fieldbyname('Feld').Asinteger := 12345;
      ZQuery.Post;
    end;
Wenn RecordCount > 0 können im Ergebnis 1 bis n Datensätz enthalten sein.
Beim Post muss nun was aktuallisiert werden, aber für Zeos bzw. die Datenbank reichen die Informationen im Datensatz nicht aus, um eindeutig einen Datensatz in der Datenbank zu identifizieren, der von der Änderung betroffen sein soll. Es wären mit den Datensatzinfos demnach <> 1 Datensätze zu ändern, also entweder 0 oder n, wobei n größer 1 ist.

Das passiert gerne dann, wenn man auf einem Client 1 einen Datensatz ändert und ein Client 2 den gleichen Datensatz zwischenzeitlich geändert hat. Der zwischenzeitlich von Client 2 geänderte Datensatz enthält nicht mehr die gleichen Informationen, wie der von Client 1 gerade zu bearbeitende / speichernde Datensatz. Deshalb scheitern Zeos und / oder Datenbank beim Speichern der Änderungen. Resultat: Deine Fehlermeldung aus dem Threadtitel.

Das passiert gerne, wenn die Transaktionssteuerung nicht so ganz sauber funktioniert (oder fehlt).

Es passiert aber auch gerne, wenn man keine eigene Transaktionssteuerung implementiert hat und in der TZConnection AutoCommit auf true steht.
Wer zuerst speichert gewinnt, der oder die Anderen bekommen Deine Fehlermeldung aus dem Threadtitel.

Weitere Ursachen möglich? Will ich mal nicht ausschließen.

erich.wanker 10. Sep 2020 13:54

AW: Meldung "0 record(s) updated. Only one record should have been updated"
 
Hallo

die "id" wird durch die Datenbank gestellt

Code:
  // ---------------- so schauts bei allen Tabellen aus
  try
    begin
      Z1.SQL.Clear;
      Z1.SQL.append('SELECT `id` FROM `meine_tabelle`');
      Z1.Open;
    end;
  except
    begin
      ASQL := 'CREATE TABLE `meine_tabelle` (' + ' `id` INT(8) UNSIGNED AUTO_INCREMENT PRIMARY KEY,'

TZConnection AutoCommit ist auf true

..

Was ich nicht ganz verstehe:
Wenn USER_A einen neuen Datensatz via ZQuery -append - post schreibt ( und die id automatisch vergeben wird)
wieso kann dann USER_B auf einmal keinen neuen Datensatz ( via ZQuery -append - post) mehr schreiben??

Delphi.Narium 10. Sep 2020 14:29

AW: Meldung "0 record(s) updated. Only one record should have been updated"
 
Wenn AutoCommit auf True steht, gehe ich mal davon aus, dass Du kein eigenes Transationshandling im Programm hast.

Wenn also zwei Clients (mehr oder weniger) zeitgleich eine Transaktion starten, dann sehen sie quasi den gleichen Datenbestand. Wenn nun ein Client seine Transaktion per AutoCommit beendet, hat er einen neuen Datenbestand. Der Client, der seine Transaktion noch nicht beendet hat, hat weiterhin seinen alten Datenbestand. Will er nun seine Transaktion schließen, weiß er nicht, was der andere Client bereits geeändert hat und speichert daher seine Daten. Die Datenbank weiß jedoch, dass sein Datenbestand von dem des anderen Clients abweicht. Für sie kann dadurch ein Konflikt entstehen.

Wenn Client 1 den Datensatz mit ID = 1 ändert und dort als Namen z. B. Müller eingibt, während Client 2 gleichzeitig den Datensatz mit ID 1 ändert und dort als Namen z. B. Meier eingibt: Wessen Daten sollen in der Datenbank stehen? Die von Client 1, weil er den Datensätz zuerst zum Ändern aufgerufen hat oder die Daten von Client 2, weil er zuerst gespeichert hat?
Oder sollen die Daten von Client 2 kommentarlos überschrieben werden, obwohl Client 1 nicht weiß, was Client 2 geändert hat?

Wollen mehrere Clients einen Datensatz ändern und die Transaktionen überschneiden sich, so wird es immer zu einem Fehler kommen. (U. a. dafür sind Transaktionen da.)
Code:
Client 1 - Start der Transaktion
Client 1 - lesen des Datensatzes
Client 2 - Start der Transaktion
Client 2 - lesen des Datensatzes
Client 1 - ändern des Datensatzes
Client 2 - ändern des Datensatzes
Client 2 - speichern des Datensatzes
Client 1 - speichern des Datensatzes
Client 2 - Commit
Client 1 - Commit
Was soll jetzt in der Datenbank stehen?
Die Änderung von Client 2, weil er zuerst das Commit gemacht hat?
Die Änderung von Client 1, weil er zuletzt das Commit gemacht hat?

Und nun das Ganze nochmal mit 25.811 Clients?

Wer soll da wann unter welchen Voraussetzungen gewinnen?

Fehlermeldung abfangen und darauf hinweisen, dass der Datensatz zwischenzeitlich anderweitig geändert wurde.
Anzeige der Änderungen anbieten, um dann mit den geänderten Daten weiterzuarbeiten oder Änderungen verwerfen (Rollback).

erich.wanker 10. Sep 2020 14:38

AW: Meldung "0 record(s) updated. Only one record should have been updated"
 
Vielen Dank für die ausführliche Hilfe. Ich bin momentan echt am verzweifeln ..

Bei "edit" würde ich ja noch verstehen dass ich irgendwie ein Problem bekomme ..

Aber ich bekomme auch bei neu zu erstellenden Datensätze diese Meldung


User 1 startet per onClick eines Buttons folgende Procedure:

ZQuery.Append;
ZQuery.fieldbyname('xx').asstring = yy
ZQuery.Post;

Wenn nun User 2 die gleiche Procedure aufruft kommt schon der Fehler ..

Wenn aber User 2 (nachdem user 1 gepostet hat) - die Software neu startet GEHTS WIEDER

P.S.:
Habe kein eigenes Transationshandling
Der zuletzt Posted - dessen Wert soll drinnen stehen.

Delphi.Narium 10. Sep 2020 14:59

AW: Meldung "0 record(s) updated. Only one record should have been updated"
 
Multiuserbetrieb ohne eigenes Transaktionshandling ist suboptimal.

Wenn Client 1 eine Transaktion startet, dann ist der nächste Wert für das AutoInc bekannt.
Wenn Client 2 eine Transaktion startet, dann ist der nächste Wert für das AutoInc bekannt.

Wenn sie das beide gleichzeitig machen, dann haben sie beim Speichern auch den gleichen Wert.

Wenn nun Client 1 zuerst speichert, dann wird der Wert vergeben.
Wenn nun Client 2 speichert, wird der gleiche Wert vergeben.

Pech gehabt: Doppelter Wert für die ID vergeben.
Resultat: Fehlermeldung.

Edit heißt nur: Das in Delphi die Daten geändert werden sollen.
Post heißt: Daten ab zur Datenbank. Dort tritt dann der Fehler auf.
Wie soll die Datenbank denn beim Edit schon wissen, dass es nach der Änderungen beim Speichern ein Problem geben könnte?

Noch witziger wird es dann beim Append:
Woher soll die Datenbank denn dort wissen, was als Daten eingegeben wird und später beim Post zu einem Problemführen könnte? Globalgalaktische :glaskugel:?

Programm beende macht letztlich (meist) ein Rollback.

Kann man sinnvoller selbst im Progamm steuern und ist dort für den Anwender weniger lässtig ;-)

erich.wanker 10. Sep 2020 15:48

AW: Meldung "0 record(s) updated. Only one record should have been updated"
 
@Delphi.Narium - vielen Dank für die ganzen Infos und für die hochprofessionelle Hilfestellung.
Vielen herzlichen Dank und ENTSCHULDIGUNG für die ganze Unannehmlichkeiten ....

Ich hab ja überall ein Feld "id" was automatisch mit einer Nummer gefüllt ist ..

Ich hab aber auch überall ein Feld "inr" was ich beim "append" händisch fülle ... :oops::oops::oops::oops::oops::oops:

nach jedem Append - egal welche Tabelle) habe ich überall einen Einzeiler: ZQuery.fieldbyname('inr').asinteger:=new_id;



Delphi-Quellcode:
function TUniMainModule.new_id: Integer;
var
* i: Integer;
begin
* GET_INR.Refresh;* * * * * * <-------- THIS SOLVED MY PROBLEM !!!!!!!

* i := GET_INR.fieldbyname('id').Asinteger;
* i := i + 1;
* GET_INR.Edit;
* GET_INR.fieldbyname('id').Asinteger := i;
* GET_INR.Post;

* Result := i;
end;
...das GET_INR.Refresh; war die Lösung


Vielen Dank
und liebe Grüße
Erich

P.S.: Gesund bleiben :-)

jaenicke 10. Sep 2020 18:01

AW: Meldung "0 record(s) updated. Only one record should have been updated"
 
Wir verwenden dafür (bei MariaDB) Generatoren. Damit wird der Wert erst beim Schreiben generiert und somit ist es egal wie lange die Transaktion vorher offen war.

EgonHugeist 10. Sep 2020 18:02

AW: Meldung "0 record(s) updated. Only one record should have been updated"
 
Hallo Erich,
zusätzlich schau da mal rein: https://sourceforge.net/p/zeoslib/tickets/270/
gleiches kann bei updates passieren, deswegen mal https://dev.mysql.com/doc/refman/8.0...cted-rows.html anschauen.
Ps. ich hab 7.3(wird 8.0) in den beta modus gesetzt. Hab Spaß!

himitsu 10. Sep 2020 18:22

AW: Meldung "0 record(s) updated. Only one record should have been updated"
 
Zitat:

Zitat von erich.wanker (Beitrag 1473402)
GET_INR.Refresh; <-------- THIS SOLVED MY PROBLEM !!!!!!!

Per se bleibt das Problem bestehen, auch wenn die kritische Zeit verkürzt wurde und somit die Wahrscheinlichkeit etwas sinkt, dass das Problem beobachtet wird.


Alle Zeitangaben in WEZ +1. Es ist jetzt 23:01 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