我在使用 Django 和 MySQL 5.5.22 时遇到以下问题。
给定一个包含 id、level 列和存储为 a11、a12、a21、a22 的 2x2 矩阵的表,我有这一行:
id a11 a12 a21 a22 level
324 3 2 5 3 2
给定一个查询集 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
应该是 True,根据这一点,该行应该是:
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 和 SQL Server)有何期望?
- 我可以使用 Django ORM(无原始查询)做什么来标准化此行为?
edit
问题很清楚,但我仍在寻找解决方案。对于此特定应用程序来说,拉出所有值并将其推回并不是可接受的解决方案。