MySQL更新更改多个列是非原子的吗?
我在MySQL 5.5.22中使用Django时遇到以下问题 给定一个包含列id、级别和2x2矩阵(存储为a11、a12、a21、a22)的表,我有以下行:MySQL更新更改多个列是非原子的吗?,mysql,sql,django,sqlite,Mysql,Sql,Django,Sqlite,我在MySQL 5.5.22中使用Django时遇到以下问题 给定一个包含列id、级别和2x2矩阵(存储为a11、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) *
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.querys获得,为简洁起见,请删除where子句):
在那之后,我的那一排看起来像这样:
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)
这真是可怕的行为,因为这是一个跨平台使用的库。我的问题是:
col2
设置为当前(更新的)col1
值,而不是原始的col1
值。结果是col1
和col2
具有相同的值。此行为不同于标准SQL
UPDATE t1 SET col1 = col1 + 1, col2 = col1;
在电视上看
我唯一的另一个想法(在Django中肯定可以实现)是将更新拆分为单独的部分,根据在早期部分中更新的字段的新(而不是旧)值定义在后续部分中更新的字段:
UPDATE storage
SET a21 = (3 * a11) + (-1 * a21),
a22 = (3 * a12) + (-1 * a22),
level = level - -1;
UPDATE storage
SET a11 = (2 * a11) + (-1 * (3*a11 - a21)),
a12 = (2 * a12) + (-1 * (3*a12 - a22));
为了防止并发问题,您应该在事务中执行这两个更新(如果RDBMS支持)。PostgreSQL、Oracle和SQL Server都将其视为一个原子操作: MySQL是唯一一个在更新后两列都包含相同值的RBDM
至于如何解决这个问题,我将从数据库中提取值,在应用程序中进行计算(而不是update语句),然后用计算出的值更新数据库。这样,您可以保证以一致的方式执行计算。这是一个有趣的问题。我在上面玩过,似乎MySQL是唯一一个这样做的。Related/duplicate:谢谢。SQL Fiddle对我来说是新的,非常有用。不幸的是,从数据库中提取所有数据并将其推回会破坏此库的全部功能。如果我愿意的话,有更好的方法去做。如果没有其他方法,很好,但我希望有更好的解决方案,即使有点黑客。谢谢参考。这就清楚了。我希望可能有一个改变这种行为的环境。好的解决方案!例如,SQL Server与插入的
和删除的伪表的工作原理相同。
UPDATE storage AS old
JOIN storage AS new USING (id)
SET
new.a21 = (3 * old.a11) + (-1 * old.a21),
new.a22 = (3 * old.a12) + (-1 * old.a22),
new.level = old.level - -1,
new.a11 = (2 * old.a11) + (-1 * old.a21),
new.a12 = (2 * old.a12) + (-1 * old.a22);
UPDATE storage
SET a21 = (3 * a11) + (-1 * a21),
a22 = (3 * a12) + (-1 * a22),
level = level - -1;
UPDATE storage
SET a11 = (2 * a11) + (-1 * (3*a11 - a21)),
a12 = (2 * a12) + (-1 * (3*a12 - a22));
CREATE TABLE Swap (
a CHAR(1),
b CHAR(1)
);
INSERT INTO Swap (a, b) VALUES ('a', 'b');
UPDATE Swap SET a = b, b = a;
SELECT * FROM Swap;