更新MySQL中与其自身连接的内部表

更新MySQL中与其自身连接的内部表,mysql,Mysql,我正试图以一种相当复杂的方式更新一个表,并决定将该表与自身进行内部连接将非常有用 (为了提供一点背景信息,该表存储通过chain_id字段链接的记录短链,并在链内按日期字段排序。更新需要以不同于其链中其他日期的方式处理具有最新日期的符号,并且可能还需要将一些链拆分为多个单独的链) 这是我所看到内容的简化版本: CREATE TABLE t( `a` INT(3) NOT NULL, `b` INT(3) ); INSERT INTO t VALUES (3,4),(3,

我正试图以一种相当复杂的方式更新一个表,并决定将该表与自身进行内部连接将非常有用

(为了提供一点背景信息,该表存储通过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…)

内部连接的条件只返回一行,因此只更新一行

> 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更新同一字段两次,即使使用相同的值,即使它有不同的别名

您可以用该语句替换update语句并靠近它,但行(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      |
+ ------ + ------ +

@CGritton解释了为什么只更新了1条记录,但没有提供确切的信息,说明在第2条记录的情况下,
b
字段设置为0的原因

解决方案的第一条线索是
set
子句有误。如果要更新两个字段,则需要使用逗号分隔这两个赋值,而不是使用
运算符。正确的说法是:

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
注意,赋值运算符的优先级较低,其次是
运算符,因此上述表达式将按如下方式执行:

SET t1.b = ((t1.a AND t2.b) = t1.a)
如果我用数字代替字段名:

SET t1.b = ((3 AND 4) = 3)
(3和4)
为0,
0=3
为0,因此
t1.b
将设置为
0


我不完全理解您希望通过查询实现什么,因此我无法为您建议最终版本。但是第一个修正版本应该是一个很好的起点。只需确保您了解联接条件。

更新具有自身内部联接的表(在我看来)是一个非常糟糕的主意。您很可能有深度未知的层次结构,因此最好的方法是(假设MySQL不支持递归查询)实现一个简单的递归函数,在每个链中找到合适的值,并在递归完全倒带时更新相应的记录。