MySQL Update Ändern mehrerer Spalten ist nicht atomar?

8

Ich habe das folgende Problem mit Django mit MySQL 5.5.22.

Wenn man eine Tabelle mit den Spalten id, level und einer 2x2-Matrix als a11, a12, a21, a22 angibt, habe ich diese Zeile:

%Vor%

Gegeben ein Abfrage-Set qs, mache ich folgendes Update:

%Vor%

Für welchen django die folgende Abfrage generiert wird (aus db.connection.queries, entferne die where-Klausel aus Gründen der Kürze):

%Vor%

Und meine Reihe sieht danach so aus:

%Vor%

Für jede Zeile, a12*a21 - a11*a22 = 1 soll True sein, und dementsprechend sollte die Zeile sein:

%Vor%

Das bekomme ich auf SQLite, wobei Django die gleiche Abfrage generiert und ich habe viel Zeit gebraucht, um herauszufinden, dass MySQL etwas anderes macht. Bei der Abfrage scheint es so zu sein, dass MySQL bei der Aktualisierung von Interdependen mehrerer Zeilen nicht als eine einzige atomare Operation behandelt wird. Wenn Spalten aktualisiert werden, wirken sie sich auf die davon abhängigen Werte aus. Ich habe bestätigt, dass dies durch den folgenden Code in der Python-Eingabeaufforderung geschieht:

%Vor%

Wenn Spalten nacheinander in derselben Reihenfolge wie die Abfrage aktualisiert werden:

%Vor%

Das ist wirklich gruseliges Verhalten, da dies eine Bibliothek ist, die plattformübergreifend verwendet werden soll. Meine Fragen sind:

  1. Was macht es falsch, MySQL oder SQLite? Kann dies als Fehler angesehen werden?
  2. Was kann ich von anderen großen Datenbanken (Oracle, PostgreSQL und SQLServer) erwarten?
  3. Was kann ich mit dem Django ORM (keine rohen Abfragen) tun, um dieses Verhalten zu normalisieren?

Bearbeiten

Das Problem ist klar, aber ich suche immer noch nach einer Lösung. Ziehen Sie alle Werte und drücken Sie sie zurück ist keine akzeptable Lösung für diese spezielle Anwendung.

    
Pedro Werneck 21.05.2012, 21:28
quelle

2 Antworten

10

Wie im MySQL-Handbuch angegeben:

  

Die zweite Zuweisung in der folgenden Anweisung setzt col2 auf den aktuellen (aktualisierten) col1 -Wert, nicht auf den ursprünglichen col1 -Wert. Das Ergebnis ist, dass col1 und col2 denselben Wert haben. Dieses Verhalten unterscheidet sich von Standard-SQL.

%Vor%

Daher ist in Ihrem Fall der Wert, der für a21 verwendet wird, wenn der Ausdruck 'a11' = (2 * 'storage'.'a11') + (-1 * 'storage'.'a21') ausgewertet wird, der neue, aktualisierte Wert von 4 und nicht der ursprüngliche Wert von 5. Wie das Handbuch sagt, dies Verhalten unterscheidet sich von Standard SQL .

Sie könnten stattdessen einen Self-Join mit der Syntax multi-table UPDATE verwenden, aber ich weiß nicht, ob so etwas mit dem Django ORM implementiert werden kann:

%Vor%

Sehen Sie es auf sqlfiddle .

Mein einziger anderer Gedanke (der definitiv in Django implementiert werden sollte) besteht darin, das Update in separate Teile aufzuteilen, die die Felder in späteren Teilen in Bezug auf die neuen (und nicht die alten) Werte dieser Felder definieren in früheren Teilen aktualisiert:

%Vor%

Um Nebenläufigkeitsprobleme zu vermeiden, sollten Sie diese beiden Aktualisierungen innerhalb einer Transaktion durchführen (falls dies vom RDBMS unterstützt wird).

    
eggyal 21.05.2012, 21:42
quelle
12

PostgreSQL, Oracle und SQL Server behandeln dies alles als atomare Operation. Siehe die folgende SQL-Fiddle und wechseln Sie den Server, um das Verhalten des folgenden SQL zu sehen :

%Vor%

MySQL war das einzige RBDMS, das dies implementiert, wobei beide Spalten nach der Aktualisierung denselben Wert enthalten.

So wie Sie dies lösen würden, würde ich stattdessen die Werte aus der Datenbank ziehen, die Berechnungen innerhalb Ihrer Anwendung durchführen (anstelle Ihrer Update-Anweisung) und dann die Datenbank mit den berechneten Werten aktualisieren. Auf diese Weise können Sie sicherstellen, dass die Berechnung konsistent durchgeführt wird.

    
Michael Fredrickson 21.05.2012 21:43
quelle

Tags und Links