使用 JOIN / 多表 UPDATE 访问同一查询中先前更新的列的行为不同

Posted

技术标签:

【中文标题】使用 JOIN / 多表 UPDATE 访问同一查询中先前更新的列的行为不同【英文标题】:Accessing a previously updated column in the same query behaves differently with JOINs / multi-table UPDATEs 【发布时间】:2017-08-02 02:08:39 【问题描述】:

假设我有两个表playersstats,内容如下:

mysql> select * from players;
+----+-------+
| id | alive |
+----+-------+
|  1 |     0 |
|  2 |     1 |
+----+-------+
mysql> select * from stats;
+--------+------+------+-------+
| player | win  | lose | ratio |
+--------+------+------+-------+
|      1 |   12 |   20 |   0.6 |
|      2 |    8 |    1 |     8 |
+--------+------+------+-------+

我想增加每个玩家的win-计数器,同时更新他们的赢/输比率。这看起来像这样:

update `stats` set `win` = `win` + 1, `ratio` = `win` / `lose`;

注意win的增量值是用来计算ratio的(正如这里的mysql手册所说:1.8.2.2 UPDATE Differences)。

现在,当将 JOIN 添加到 UPDATE 查询以限制它仅更新 alive = 1 的玩家时,此行为会发生变化:

update `stats` st
inner join `players` pl
    on ( pl.`id` = st.`player` )
set `win` = `win` + 1, `ratio` = `win` / `lose`
where pl.`alive` = 1;

mysql> select * from stats;
+--------+------+------+-------+
| player | win  | lose | ratio |
+--------+------+------+-------+
|      1 |   12 |   20 |   0.6 |
|      2 |    9 |    1 |     8 |
+--------+------+------+-------+

我找到的唯一解决方案是将 win 的新值分配给一个临时变量,并在计算 ratio 时使用该临时变量:

update `stats` st
inner join `players` pl
    on ( pl.`id` = st.`player` )
set
    `win` = @tmpWin := ( `win` + 1 ),
    `ratio` = @tmpWin / `lose`
where pl.`alive` = 1;

为什么 MySQL 会出现这样的行为,对于此类问题是否有更优雅的解决方案(除了创建一个动态计算比率的视图)?

表的创建如下:

create table `players` (
    `id`        INT,
    `alive`     TINYINT,
    primary key (`id`)
);

create table `stats` (
    `player`    INT,
    `win`       INT,
    `lose`      INT,
    `ratio`     FLOAT,
    primary key (`player`)
);

我正在使用 MySQL v5.7.17

【问题讨论】:

在 5.7.17 中,您可以使用 generated columns。 【参考方案1】:

我无法解释确实看起来很奇怪的连接案例的行为,但以下内容按预期工作:

 UPDATE `stats` 
 SET `win` = `win` + 1, `ratio` = `win` / `lose` 
 WHERE player IN (SELECT id FROM players WHERE alive=1);

【讨论】:

以上是关于使用 JOIN / 多表 UPDATE 访问同一查询中先前更新的列的行为不同的主要内容,如果未能解决你的问题,请参考以下文章

MySQL多表更新

性能调优7:多表连接 - join

Mysql的join语句

怎样使数据库中的多表实现联合查询

关于mysql,需要掌握的基础:CRUD存储引擎单表查询相关多表查询join事务并发权限管理等等

Access的Left join多表联合查询