我使用Django與MySQL 5.5.22有以下問題。更新多列的MySQL更新是非原子的?
給出列編號,級別和存儲A11一個2x2矩陣,A12,A21,A22的表,我有這樣的一行:
id a11 a12 a21 a22 level
324 3 2 5 3 2
給定一個queryset的QS,我做了以下更新:
qs.update(
a11=(b12 * a21 - b11 * a22) * F('a11') + (b11 * a12 - b12 * a11) * F('a21'),
a12=(b12 * a21 - b11 * a22) * F('a12') + (b11 * a12 - b12 * a11) * F('a22'),
a21=(b22 * a21 - b21 * a22) * F('a11') + (b21 * a12 - b22 * a11) * F('a21'),
a22=(b22 * a21 - b21 * a22) * F('a12') + (b21 * a12 - b22 * a11) * F('a22'),
level=(F('level') - 1)
)
對於其中的django生成以下查詢(從db.connection.queries得到它,除去where子句爲了簡潔):
UPDATE `storage`
SET
`a21` = (3 * `storage`.`a11`) + (-1 * `storage`.`a21`),
`a22` = (3 * `storage`.`a12`) + (-1 * `storage`.`a22`),
`level` = `storage`.`level` - -1,
`a11` = (2 * `storage`.`a11`) + (-1 * `storage`.`a21`),
`a12` = (2 * `storage`.`a12`) + (-1 * `storage`.`a22`)
而且我行看起來像這樣之後:
id a11 a12 a21 a22 level
324 2 1 4 3 1
對於任何行,a12*a21 - a11*a22 = 1
應該是真實的,根據的是,該行應該是:
id a11 a12 a21 a22 level
324 1 1 4 3 1
這是我在SQLite上得到的結果是,Django生成相同的查詢,並且花了我很多時間來確定MySQL正在做一些不同的事情。從查詢來看,似乎在更新相互取消的多行時,MySQL不會將其視爲單個原子操作,並且隨着列更新,它們會影響取決於它們的值。我證實了這一點似乎正是通過下面的代碼在Python提示符發生了:
>>> a11, a12, a21, a22 = (3, 2, 5, 3)
>>> (2 * a11) + (-1 * a21),\
... (2 * a12) + (-1 * a22),\
... (3 * a11) + (-1 * a21),\
... (3 * a12) + (-1 * a22)
(1, 1, 4, 3)
如果列被更新一次一個,在該查詢給出相同的順序:
>>> a11, a12, a21, a22 = (3, 2, 5, 3)
>>> a21 = (3*a11) + (-1*a21)
>>> a22 = (3*a12) + (-1*a22)
>>> a11 = (2*a11) + (-1*a21)
>>> a12 = (2*a12) + (-1*a22)
>>> (a11, a12, a21, a22)
(2, 1, 4, 3)
這是一個非常可怕的行爲,因爲這是一個圖書館,意味着要使用跨平臺。我的問題是:
- 哪一個是做錯了,MySQL或SQLite?這可以被認爲是一個錯誤?
- 我可以從其他主要數據庫(Oracle,PostgreSQL和SQLServer)期望什麼?
- 如何使用Django ORM(無原始查詢)對此行爲進行規範化?
編輯
的問題是明確的,但我仍然在尋找一個解決方案。提取所有值並將其推回對於此特定應用程序來說不是可接受的解決方案。
這是一個有趣的問題。我在[sqlfiddle](http://sqlfiddle.com/#!2/7f14b/2)上玩過它,似乎MySQL是唯一這樣表現的人。 – Chad
相關/重複:http://stackoverflow.com/questions/2203202/sql-update-order-of-evaluation – pilcrow
看到我下面更新的答案。 – eggyal