![]() |
Datenbank: MSSQL Server • Version: xy • Zugriff über: TADO
Eigenartiger Effekt mit int-Primary Keys ?
Hallo Delphi-Gemeinde,
ich habe eine Anwendung geschrieben, welche mittler weile seit über 10 Jahren mehr oder weniger erfolgreich läuft. Eher mehr ;-) Ich nutze zum Erstellen der Primärschlüssel eine eigene Funktion, welche aus einer Primärschlüssel-Tabelle einen Integer-Wert ermittelt, diesen um Eins hoch zählt ... usw. Die AutoIdent-Funktion des MsSQL-Servers nutze ich nicht, da meine Anwendung teilweise auch auf anderen Datenbanken (z.B. dBase, Clipper ...) läuft, welche AutoIdent-Funktionen nicht kennen. Zum Löschen von Datensätzen gibt es in meiner Anwendung eine Regel: Der Primärschlüssel wird negativ gesetzt und somit ist der Datensatz als gelöscht markiert. Kunden-Anforderung! (Aufgeräumt wird später).
Code:
TransactionLook ist aktiviert.
UPDATE tabelle SET id=ABS(id)*-1 WHERE id=:ID;
Mit dieser Regel läuft meine Anwendung seit Anfang an eigentlich recht stabil. Mittlerweile ist meine Anwendung soweit fortgeschritten, dass es in einigen Kundeninstallationen einen Primärschlüssel im 5 Mio-Bereich verwendet. Und natürlich ist die Anwendung mittlerweile so groß, dass es sehr schwer ist, SQL-Fehler zu identifizieren. Voll allem dann, wenn es sich um einen Folgefehler handelt. Das Problem: Der SQL-Befehl:
Code:
, wobei ID mit "5366038" belegt ist, schlägt fehl, da ein doppelter Primärschlüssel nicht zugelassen ist.
UPDATE tabelle SET id=ABS(id)*-1 WHERE id=:ID;
Das heißt, es soll eine Datensatz gelöscht markiert werden, welcher bereits gelöscht markiert ist. Und tatsächlich existiert zwei Datensätze, jeweils mit einem positiven ID und einem "gleichen" negativen ID. Das das nicht klappt ist klar. Interessanter Weise tritt dieser Fehler NUR im MSSQL-2008R2 und MSSQL-2012 auf und immer bei den gleichen Schlüsseln 5366038 bzw. 5365874. Ich habe ca. 20 Installationen am laufen, die diesen Fehler produziert haben. Und es betrifft immer einen dieser beiden Schlüssel. Ich habe das ganze Programm durchwühlt und nach einer Ursache im Code gesucht, konnte aber nichts finden. Aber Tatsache, dass dieser Fehler immer bei den gleichen IDs auftritt, bringt mir zu der Überzeugung, dass der Fehler nicht in meinem Programm-Code zu suchen ist, sondern wo anders. Von Visual Studio 20xy MFC-Klassen kenne ich das Phänomen, dass dort ein Integer-Wert festgelegt wurde, welcher quasi als NULL definiert ist. Das war ein Bug von M$, welcher irgendwie gefixt wurde. So soll (wenn ich das richtig verstanden haben) der (z.B.) SQL-Befehl:
Code:
vom MSSQL-Server als
UPDATE tabelle SET id=ABS(id)*-1 WHERE id=5365874;
Code:
übersetzt/interpretiert worden sein.
UPDATE tabelle SET id=ABS(id)*-1 WHERE id =NULL;
Kann es sein, dass in den Delphi-ADO-Klassen ein ähnliches Phänomen existiert? Meine IDE: Delphi-2009 auf MS-Server 2008-R2. Die Parameter (:ID) werden mit SetParam gesetzt, nicht direkt im SQL-Statement. Die Verbindung zum SQL-Server wird über einen eigenen ConnectionString per SQLOLEDB aufgebaut. Habt ihr ähnliche Erfahrungen? |
AW: Eigenartiger Effekt mit int-Primary Keys ?
Hallo,
dass es "versteckte" Integer-Werte gibt, habe ich noch nie gehört. Quelle ? Dein Problem solltest du doch an Deinem Rechner nachstellen können? Ich denke eher, dass du Ids doppelt vergeben hast. Genau dafür ist ja das Identity zumindestens beim MS-SQL da. Andere DBs haben halt andere Mittel, um eindeutige Werte zu erzeugen. Zumindestens sollte jedes DB einen Unique Index anbieten, wobei das bei Dir nicht geholfen hätte (wegen dem negativ machen). Heiko |
AW: Eigenartiger Effekt mit int-Primary Keys ?
Vorschläge habe ich nicht, nur drei Fragen:
1. Wie markierst/löschst Du Fremdschlüssel? 2. Wieso setzt Du die ID auf einen negativen Wert, anstatt einfach ein 'Deleted'-Flag zu pflegen? 3. Welchen transaction Isolation level verwendest Du? |
AW: Eigenartiger Effekt mit int-Primary Keys ?
Nach der Beschreibung im Eröffnungsposting kann ich mir die Sache nicht anders erklären: Irgendwann wird ein PK negativ zu setzen versucht, wobei eine Kopie des Records mit dem positiven PK entsteht.
Vielleicht kann der DB-Server gar nicht anders handeln, wenn z.B. FKs in anderen Tabellen existieren, die auf diesen "positiven" PK deiner problematischen Tabelle verweisen, so daß dieser gar nicht gelöscht werden kann? Könntest du eventuell mal nachprüfen, ob irgendwelche FKs anderer Tabellen diese beiden problematischen Records über ihre PKs referenzieren? Zusätzlich würde ich einmal versuchen, einen neuen Datensatz mit diesem problematischen PK anzulegen und danach zu löschen, und zwar debuggenderweise. Damit solltest du eigentlich punktgenau auf die Stelle stoßen, die den Fehler auslöst. Beobachten oder gar nachvollziehen konnte ich dieses Verhalten bislang jedoch nicht (MsSQL, Firebird, MySQL). Wenn ich mal viel Zeit und Muse habe, probier ich's vielleicht mal aus ... |
AW: Eigenartiger Effekt mit int-Primary Keys ?
Zitat:
dann würde das Problem gleich beim zweiten ANLEGEN und nicht erst beim LÖSCHEN auffallen. Zitat:
|
AW: Eigenartiger Effekt mit int-Primary Keys ?
Zitat:
![]() Das dieser Effekt/Bug tatsächlich existiert haben ich live gesehen! Geschrieben ist das Ganze im VS2010 in der afxdb.h Zitat:
Zitat:
Aber die 5 Mio Datensätze davor machen auch keine Probleme. Und in diesen 5 Mio Datensätzen sind ca. 20% mit negativem ID. Ja, gelöscht wird viel! Ein Großteil davon durchaus berechtigt. Zitat:
Delphi-Quellcode:
ruft?
TAdoConnection.BeginTrans
Zitat:
Code:
Ein Unique Index über tbbame,idname existiert
// PseudoCode
TAdoCon.BeginTrans; UPDATE nidents SET id=id+1 WHERE tbname=:TableName AND idname=:PkName; SELECT id from nidents where tbname=:TableName AND idname=:PkName; TAdoCon.CommitTrans; |
AW: Eigenartiger Effekt mit int-Primary Keys ?
Hallo,
nun das Bsp. beschreibt aber, dass der Nutzer die falsche Dummy-Variable benutzt hat. Murks ist das trotzdem ... Ich wollte doch aber wissen, ob du das nachstellen kannst. Heiko |
AW: Eigenartiger Effekt mit int-Primary Keys ?
Zitat:
Zitat:
Grundsätzlich halte ich auch weiterhin für problematisch, wenn man ohne ausreichende Grundkenntnisse versucht, funktionierende Anwendungen für ein produktives Umfeld schreiben zu wollen. |
AW: Eigenartiger Effekt mit int-Primary Keys ?
Zitat:
Zitat:
Zitat:
Um diese Frage zu beantworten, verwende den Profiler, oder schau Dir die Eigenschaften der Connection an. Wenn Du weißt, wie der Isolationlevel ist, dann weißt Du auch, wie sich der Server beim konkurrierenden Lesen verhält. Natürlich ist das ärgerlich und irgendwo ist der Wurm. Aber ich lege meine Hand ins Feuer, das der SQL-Server mit NULL-Werten richtig umgehen kann. Zeig doch mal die Definition der Tabelle mit den doppelten Ids. Wichtig ist auch die Definition vom Index sowie die Definition der 'idents-Tabelle'. Beim SSMS gibt es die Funktion 'Generate Script'. Dort musst Du dann die eine Tabelle auswählen und in den Eigenschaften (ein Button auf der 3. Seite) noch einstellen, das auch der Index geskriptet werden soll. Das stellst Du dann hier ein und dann schauen wir weiter. Du kannst versuchen, dein Updatestatement sicher zu machen, indem Du die 'OUTPUT' Klausel verwendest (obwohl Du damit nicht mehr kompatibel zu dBase bist).
Code:
Damit hast Du eine atomare Anweisung, die garantiert transaktionssicher ist. Allerdings sind Probleme aufgrund der Nebenläufigkeit i.a. zufälliger Natur, d.h. Du müsstest eher zufällige doppelte Schlüssel beobachten.
UPDATE nidents
SET id=id+1 OUTPUT id WHERE tbname=:TableName AND idname=:PkName; Auch wenn es zum Haareraufen ist: Meist sitzt der Fehler 80cm vom Bildschirm entfernt. Nebenbei: Wer will heutzutage noch kompatibel zu dbase sein, wo es doch freie Datenbanken gibt? |
AW: Eigenartiger Effekt mit int-Primary Keys ?
Zunächst einmal, alle Schlüssel dienen der internen Verwaltung von Daten. Wer von extern darauf zugreift und diese manipuliert gehört geteert und gefedert.
Was deinen Fehler angeht, wenn der/die Schlüssel bereits existieren, dann arbeitet Dein Schlüsselgenerator eben nicht zuverlässig, da das nicht auftreten darf. Was heißt überhaupt negativ setzen? id:=id*-1 oder id:=id or $F000000 ? Und wieviele Bits haben Deine Integers eigentlich? Wenn auch DBase versorgt werden muß, konnte das eigentlich schon mit 32Bit-Werten umgehen? Gruß K-H |
AW: Eigenartiger Effekt mit int-Primary Keys ?
Zitat:
@gmc616 Hast Du auch den richtigen Code statt den Pseudocode? Der Isolationlevel kann ein Problem sein, wenn auch nicht commitete Werte gelesen werden. Was passiert, wenn Du eine AdoQuery auf ein neues Formular setzt und genau diese Statements ausführst? Wie alt/neu sind Deine ADO Treiber und die der Kunden? Was geschieht mit Werten, die über diese problematischen Werte hinausgehen? |
AW: Eigenartiger Effekt mit int-Primary Keys ?
Dem schließe ich mich an :thumb::kiss: (Letzteres ist rein platonisch gemeint, nicht dass da jemand falsche Schlüsse zieht).
|
AW: Eigenartiger Effekt mit int-Primary Keys ?
Zitat:
Oder wollen wir uns doch mal treffen DeddyH? :stupid: |
AW: Eigenartiger Effekt mit int-Primary Keys ?
Ups. Mein Fehler - der Beitrag von DeddyH hätte mit abgetrennt werden müssen.
Sorry dafür. :oops: |
AW: Eigenartiger Effekt mit int-Primary Keys ?
Kein Problem meinetwegen, ist schließlich Karneval. Ich weiß natürlich nicht wie DeddyH das sieht.
:) @Abtrennung: Hab ich nun auch endlich geschnallt, dass das Thema "Umgang" verlagert wurde. ![]() |
AW: Eigenartiger Effekt mit int-Primary Keys ?
Zitat:
Gibt es Installationen mit diesen Datenbanken, bei denen
Single-User-Betrieb oder Multi-User-Betrieb? Um besser Hilfestellung geben zu können, wäre es eventuell hilfreich, wenn Du uns eine Liste der genutzten Datenbanken geben könntest. Für eine Problemlösung für unterschiedliche Datenbanken muss letztlich SQL-technisch der kleinste gemeinsame Nenner genutzt werden, um Dein Programm kompatibel zu allen genutzten Datenbanken zu halten. *** Gehen wir nochmal von Deinem Code aus:Aktuell sei ID = 5366037
Code:
Dann liefert das Select für ID den Wert 5366038.
// PseudoCode
TAdoCon.BeginTrans; UPDATE nidents SET id=id+1 WHERE tbname=:TableName AND idname=:PkName; SELECT id from nidents where tbname=:TableName AND idname=:PkName; TAdoCon.CommitTrans; Der Satz wird nun gelöscht:
Code:
ID ist nun -5366038.
UPDATE tabelle SET id=ABS(id)*-1 WHERE id=5366038;
Würde vom MSSQL-Server ausgeführt
Code:
so wäre ID weiterhin 5366038, dem Satz fehlt in diesem Fall nur die Löschmarkierung.
UPDATE tabelle SET id=ABS(id)*-1 WHERE id =NULL;
Eine ID-Dublette kann hier daher nicht entstanden sein. Eine ID-Dublette kann nur dann entstehen, wenn obiger "PseudoCode" zweimal das gleiche Ergebnis liefert. Da nicht bekannt ist, wie der tatsächliche Programmablauf erfolgt, könnte hier aber ein Problem vorliegen. Wenn mehrere Benutzer (und / oder mehrere Threads) gleichzeitig diesen Code ausführen, kann es zu Dubletten kommen. Beim Lesen der ID per Select ist der mit Update gesetzte Wert noch nicht in der Datenbank festgeschrieben, theoretisch könnte ein zweiter User annähernd zeitgleich daher zum gleichen Ergebnis kommen. Meiner Meinung nach wäre es sinnvoller, zuerst die ID in einer eigenen Transaktion in der Tabelle nidents zu erhöhen und erst nach dem Commit per SQL auszulesen, aber 100% sicher erscheint mir diese Lösung auch nicht.
Code:
Irritierend finde ich allerdings schon, dass der Fehler nur bei zwei Datenbanktypen mit zwei bestimmten IDs aufgetreten ist.
// PseudoCode
TAdoCon.BeginTrans; UPDATE nidents SET id=id+1 WHERE tbname=:TableName AND idname=:PkName; TAdoCon.CommitTrans; SELECT id from nidents where tbname=:TableName AND idname=:PkName; |
AW: Eigenartiger Effekt mit int-Primary Keys ?
Zitat:
(vor vielen Jahren war es Zeichen besonders cleveren Programmierens 16Bit-Words zu nutzen, die gehen ja bis 65.000 und soooviele Datensätze haben wir nieeee. Abgesehen von der etwas kurzsichtigen Sichtweise was die Datenmenge angeht, was passiert wenn auf diesen 16Bit-Wert mal als Word und mal als Integer zugegriffen wird??) Gruß K-H |
AW: Eigenartiger Effekt mit int-Primary Keys ?
Bei 20 Installationen ist das kein Zufall mehr. Es scheint doch so zu sein, das es bei allen Installationen auffällt. Ich tippe ja auch auf die Software selbst, aber das muss schon ein merkwürdiger Zufall sein.
Code:
Gemein ist das in jedem Fall.
Update Tabelle set Id=5366038 where ....
|
AW: Eigenartiger Effekt mit int-Primary Keys ?
Zitat:
ein
Code:
ginge ja zur not noch aber ein Update auf einen Datensatz der keinen Schlüssel hat??
Insert into Tabelle (id) values(12345)
Gruß K-H |
AW: Eigenartiger Effekt mit int-Primary Keys ?
Nein, ich meine 'irgendetwas Gemeines'. Also etwas, das richtig fies ist. Gemein. Unerwartet. Zum-Hand-Gegen-die-Stirn-klatschen. Oder-Kopf-gegen-die-Mauer.
PS: Die Tabelle hat ja einen Schlüssel. Das 'Statement' (das es so ja bestimmt nicht gibt) kann ja anders aussehen, z.B.
Delphi-Quellcode:
Anders kann ich mir das nicht erklären. Oder er überschreibt Field.SetText. Oder. Oder. Oder.
//Anstatt
Datensatz.Zahlenfeld := 5366038; // Steht da eben Datensatz.Id := BloedeFunktionDie5366038Ergibt(); Datensatz.Post; Ärgerlich, sowas. |
AW: Eigenartiger Effekt mit int-Primary Keys ?
Hallo,
da fällt mir ein Fehler aus meiner Jugendzeit ein (Paradox-"Datenbank" mit 20 Nutzern im Netz). Ab und zu waren die Indizes zerschossen und ich habe 3 Rechner parallel ständig Daten einfügen/löschen lassen, konnte das aber nicht reproduzieren. Der Isolation-Level spielt beim SQL-Server ja wirklich eine Rolle, dass ist bei "meinem" FireBird viel einfacher. Noch mal die Frage an den TE: Das trat jetzt bei wie vielen Kunden/Installationen auf? Heiko |
Alle Zeitangaben in WEZ +1. Es ist jetzt 04:13 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-2025 by Thomas Breitkreuz