我试图以一种相当复杂的方式更新表,并决定将表连接到自身是有用的。
(为了给出一些背景知识,该表存储通过chain_id字段链接的短链记录,并在链中按日期字段排序。更新需要处理最近日期与其他日期不同的符号在它的链中,也可能需要将一些链分成多个独立的链)。
这是我所看到的简化版本:
CREATE TABLE t(
`a` INT(3) NOT NULL,
`b` INT(3)
);
INSERT INTO t VALUES (3,4),(3,6);
SELECT * FROM t;
------------
| a | b |
-------------
| 3 | 4 |
| 3 | 6 | as expected
UPDATE t t1
INNER JOIN t t2
ON (t1.a = t2.a
AND t1.b > t2.b)
SET t1.b = t1.a
AND t2.b = t1.a;
SELECT * FROM t;
------------
| a | b |
-------------
| 3 | 4 |
| 3 | 0 | wat
我希望得到的是:
------------
| a | b |
-------------
| 3 | 3 |
| 3 | 3 |
很明显这是一个糟糕的计划,但给我留下了三个问题:
1。)这里发生了什么?
2。)因为无论发生什么都不可能是预期的,为什么MySQL会允许它呢?
3。)是否有另一种方法来实现我想要的东西(即实际更新一个与自身结合的表格内部,而不仅仅是将所有内容设置为3 ......)
答案 0 :(得分:2)
@CGritton解释了为什么只更新了1条记录,但没有提供关于为什么b
字段在第2条记录中被设置为0的确切信息。
解决方案的第一个线索是您错误地发现了set
子句。如果要更新2个字段,则需要使用逗号分隔两个分配,而不是使用and
运算符。正确的陈述是:
UPDATE t t1
INNER JOIN t t2
ON (t1.a = t2.a
AND t1.b > t2.b)
SET t1.b = t1.a, t2.b = t1.a;
如果我们分析原始的set
条款:
SET t1.b = t1.a AND t2.b = t1.a
注意,赋值运算符的优先级较低,然后是and
运算符,因此上面的表达式将执行为:
SET t1.b = ((t1.a AND t2.b) = t1.a)
如果我用数字代替字段名称:
SET t1.b = ((3 AND 4) = 3)
(3 AND 4)
为0,0 = 3
为0,因此t1.b
将设为0
我不完全了解您希望通过查询实现的目标,因此我无法为您推荐最终版本。但第一个更正的版本应该是一个很好的起点。只需确保您了解加入条件。
答案 1 :(得分:1)
内连接的条件只返回一行,因此只更新了一行。
> select *
from t t1
INNER JOIN t t2
ON (t1.a = t2.a
AND t1.b > t2.b)
+ ------ + ------ + ------ + ------ +
| a | b | a | b |
+ ------ + ------ + ------ + ------ +
| 3 | 6 | 3 | 4 |
+ ------ + ------ + ------ + ------ +
至于0的原因,请查看:
create table two_updates (a int(3));
insert into two_updates values (1);
update two_updates set a = 99 and a = 99;
select * from two_updates;
+ ------ +
| a |
+ ------ +
| 0 |
+ ------ +
在您的查询中,您要求MySQL更新相同的字段两次,即使具有相同的值,即使它具有不同的别名。
您可以将此更新语句替换为此更近,但行(3,4)将不会更新,因为它不是内部联接的一部分。
UPDATE t t1
INNER JOIN t t2
ON (t1.a = t2.a
AND t1.b > t2.b)
SET t1.b = t1.a;
+ ------ + ------ +
| a | b |
+ ------ + ------ +
| 3 | 4 |
| 3 | 3 |
+ ------ + ------ +