出自:http://hedengcheng.com/?p=209

两表join的multi update语句,执行结果与预计不一致的分析过程

— multi update结论
在实际应用中,不要轻易使用multi update更新,根据join的不同顺序,
更新的结果也会发生变化,multi update不是一个有稳定输出的语句,
并且输出结果很难理解,最好不用!

整个测试的准备与multi update的处理流程分析,请见下

— T1表
DROP TABLE IF EXISTS `t1`;

CREATE TABLE `t1` (
`aid` int(10) DEFAULT NULL COMMENT ‘编号’,
`name` varchar(32) DEFAULT NULL COMMENT ‘名称’,
`total` int(10) DEFAULT NULL COMMENT ‘剩余数量’,
`unit` varchar(32) DEFAULT NULL COMMENT ‘数量单位’
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `t1` VALUES (1001,’商品a’,100,’个’),(1002,’商品b’,100,’个’),
(1003,’商品c’,100,’个’);

mysql> select * from t1;
+——+——-+——-+——+
| aid | name | total | unit |
+——+——-+——-+——+
| 1001 | 商品a | 100 | 个 |
| 1002 | 商品b | 100 | 个 |
| 1003 | 商品c | 100 | 个 |
+——+——-+——-+——+
3 rows in set (0.00 sec)

— T2表
DROP TABLE IF EXISTS `t2`;

CREATE TABLE `t2` (
`bid` int(10) DEFAULT NULL COMMENT ‘编号’,
`aid` varchar(32) DEFAULT NULL COMMENT ‘商品编号’,
`used` int(10) DEFAULT NULL COMMENT ‘使用数量’,
`status` varchar(32) DEFAULT NULL COMMENT ‘状态’
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `t2` VALUES (1,’1001′,1,’WAIT’),(1,’1002′,3,’WAIT’),
(1,’1001′,9,’WAIT’),(2,’1003′,1,’DONE’);

mysql> select * from t2;
+——+——+——+——–+
| bid | aid | used | status |
+——+——+——+——–+
| 1 | 1001 | 1 | WAIT |
| 1 | 1002 | 3 | WAIT |
| 1 | 1001 | 9 | WAIT |
| 2 | 1003 | 1 | DONE |
+——+——+——+——–+
4 rows in set (0.00 sec)

— multi update语句
UPDATE t1,t2
SET t1.total = t1.total- t2.used, t2.status =’DONE’
WHERE t1.aid = t2.aid
AND t2.bid = ‘1’
AND t2.status = ‘WAIT’;

— multi update语句执行结果
Query OK, 5 rows affected (19 min 22.43 sec)
Rows matched: 5 Changed: 5 Warnings: 0

mysql> select * from t1;
+——+——-+——-+——+
| aid | name | total | unit |
+——+——-+——-+——+
| 1001 | 商品a | 99 | 个 |
| 1002 | 商品b | 97 | 个 |
| 1003 | 商品c | 100 | 个 |
+——+——-+——-+——+
3 rows in set (2.38 sec)

mysql> select * from t2;
+——+——+——+——–+
| bid | aid | used | status |
+——+——+——+——–+
| 1 | 1001 | 1 | DONE |
| 1 | 1002 | 3 | DONE |
| 1 | 1001 | 9 | DONE |
| 2 | 1003 | 1 | DONE |
+——+——+——+——–+
4 rows in set (0.00 sec)

— 执行结果简单分析
1. 根据update语句,此时t1表的aid=1001的项,total为什么=99,
而不是90,或者是91?
2. update执行结果,一共影响了5行记录,是哪5行?
3. update执行的顺序是怎么样的?

— multi update过程跟踪
表名                                 更新前项                    更新后项
update 1:  t1                 (1001, 商品a, 100)         (1001, 商品a, 99)
update 2:  t1                 (1002, 商品b, 100)         (1002, 商品b, 97)
update 3:  t2                 (1, 1001, 1, WAIT)         (1, 1001, 1, DONE)
update 4:  t2                 (1, 1001, 9, WAIT)         (1, 1001, 9, DONE)
update 5:  t2                 (1, 1002, 3, WAIT)         (1, 1002, 3, DONE)

— multi update流程分析(根据源码跟踪调试总结而来)

1. 判断出是multi update,为每一个可能更新的表,创建内存临时表
(http://fendou.org/2009/01/20/mysql-heap/)
此处需要为t1,t2表创建内存临时表(临时表使用的是heap引擎)

2. 对t1,t2表做join查询,t1作为join的外表,t2作为join的内表

3. 对于join到的t1,t2项,根据update条件构造更新后项,分别插入
对应的t1,t2内存临时表(调用ha_heap.cc::ha_heap::write_row方法)
3.1 对于t1表的第1条记录与t2表的第1条记录,join成功,因此将对应
的t1更新后项(rowid1_1, 99)(rowid1_1代表t1表的第1条记录,Unique)
与t2更新后项(rowid2_1, DONE)插入内存表

4. 插入内存表时,会进行unique判断,每条记录只能被插入内存表一次。
因此,对于t1的第1条记录与t2的第3条记录,join成功。但是由于t1
表的内存临时表中已经存在(rowid1_1, 99),因此插入(rowid1_1, 91)失败,
Unique冲突。t2表的更新后项(rowid1_3, DONE)插入临时表能够成功。

5. 整个join过程完成,t1临时表中的记录为(rowid1_1, 99),(rowid1_2, 97);
t2临时表中的记录为(rowid2_1, DONE),(rowid2_3, DONE),(rowid2_2, DONE)

6. sql_update.cc::multi_update::do_updates函数,遍历t1,t2的临时表,构造
完整的更新后项,分别对t1,t2表记录进行更新。最后的更新结果,就是t1表
更新了2行,t2表更新了3行,使用的是临时表中的更新后项。与最终的update
执行结果,完全一致。

— multi update结论
在实际应用中,不要轻易使用multi update更新,根据join的不同顺序,更新
的结果也会发生变化,multi update不是一个有稳定输出的语句,并且输出结果
很难理解,最好不用!

[转]两表join的multi update语句在MySQL中的执行流程分析的更多相关文章

  1. 一条SQL语句在MySQL中如何执行的

    本篇文章会分析一个 sql 语句在 MySQL 中的执行流程,包括 sql 的查询在 MySQL 内部会怎么流转,sql 语句的更新是怎么完成的. 在分析之前我会先带着你看看 MySQL 的基础架构, ...

  2. 一条 SQL 语句在 MySQL 中如何执行的

    一 MySQL 基础架构分析 1.1 MySQL 基本架构概览 下图是 MySQL 的一个简要架构图,从下图你可以很清晰的看到用户的 SQL 语句在 MySQL 内部是如何执行的. 先简单介绍一下下图 ...

  3. sql语句在Mysql中如何执行?

    1.MySQL 主要分为 Server 层和引擎层,Server 层主要包括连接器.查询缓存.分析器.优化器.执行器,同时还有一个日志模块(binlog),这个日志模块所有执行引擎都可以共用,redo ...

  4. 一条SQL语句在MySQL中是如何执行的

    概览 本篇文章会分析下一个sql语句在mysql中的执行流程,包括sql的查询在mysql内部会怎么流转,sql语句的更新是怎么完成的. 一.mysql架构分析 mysql主要分为Server层和存储 ...

  5. 原来select语句在MySQL中是这样执行的!看完又涨见识了!这回我要碾压面试官!

    大家好,我是冰河~~ MySQL作为互联网行业使用最多的关系型数据库之一,与其免费.开源的特性是密不可分的.然而,很多小伙伴工作了很多年,只知道使用MySQL进行CRUD操作,这也导致很多小伙伴工作多 ...

  6. 一条查询语句在MySQL中是如何执行的?

    前言 我们在学习一种技术的时候,首先要鸟瞰其全貌,千万不要一开始就陷入到细节中去,这样有助于我们站在高维度其理解问题 —— 丁奇. 学习MySQL也是一样,所以我们可以从一条查询语句的执行开始看起. ...

  7. [转]数据库中间件 MyCAT源码分析——跨库两表Join

    1. 概述 2. 主流程 3. ShareJoin 3.1 JoinParser 3.2 ShareJoin.processSQL(...) 3.3 BatchSQLJob 3.4 ShareDBJo ...

  8. mysql中SQL执行过程详解与用于预处理语句的SQL语法

    mysql中SQL执行过程详解 客户端发送一条查询给服务器: 服务器先检查查询缓存,如果命中了缓存,则立刻返回存储在缓存中的结果.否则进入下一阶段. 服务器段进行SQL解析.预处理,在优化器生成对应的 ...

  9. (转)MapReduce 中的两表 join 几种方案简介

    1. 概述 在传统数据库(如:MYSQL)中,JOIN操作是非常常见且非常耗时的.而在HADOOP中进行JOIN操作,同样常见且耗时,由于Hadoop的独特设计思想,当进行JOIN操作时,有一些特殊的 ...

随机推荐

  1. maven中导入包版本冲突的解决

    导入struts包,在struts核心包的ognl包下存在javassist包: 然后再导入hibernate包,在hibernate核心下也存在javassist包: 这样便会存在冲突,ecplis ...

  2. 【gRPC使用问题1】gRPC的proto内import其他proto导致的一次小坑

    1.对于一些proto里面的定义,如果包含了 引入其他proto文件的 proto文件来说,生成的时候要注意.尤其是 引入的是官方框架内的 proto文件,如果自己没有提供的话,生成代码会报错! 具体 ...

  3. EF语句拦截器-匹配当前的Controller,Action,User

    示例代码,ps:一切都能实现,关键是你尝试的方向,别把简单问题复杂化导致进入死胡同出不来. using Mobile360.Core.Interfaces; using Mobile360.Core. ...

  4. Linux 编译时内存不足

    1.编译内核出现问题:No space left on device AS      .tmp_kallsyms1.o .tmp_kallsyms1.S:2: fatal error: when wr ...

  5. 偷懒把本来要判断输入值的textbox 输出提示值,结果点两次程序异常

    调试程序是苦并快乐着的事, 为防止用户直接点击导入下表,于是我设置提示,点击时先判断textbox 里边有无值,为空的话也在textbox做提示 程序如下: 点一次可以正常输出到textbox中,点第 ...

  6. (转)Android学习-使用Async-Http实现图片压缩并上传功能

    (转)Android学习-使用Async-Http实现图片压缩并上传功能 文章转载自:作者:RyaneLee链接:http://www.jianshu.com/p/940fc7ba39e1 让我头疼一 ...

  7. Android.Tools.Summary

    Android平台上工具的总结 每个工具的详细使用和深入理解参考每个工具相关的blog. 1. Android SDK中提供的工具 http://developer.android.com/tools ...

  8. 码代码的小女孩(来自noip贴吧)

    天冷极了,下着雪,又快黑了.这是NOIP的前夜.在这又冷又黑的晚上,一个衣衫破烂的小女孩在机房敲着代码.她从班里逃出来的时候还拿着一本算导,但是有什么用呢?那是一本很破旧的书--那么大,一向是她妈妈垫 ...

  9. compatible with

    和系统函数冲突

  10. String... to 可变参数的使用

    public class testMail { public static void fun(int... x) { for(int i = 0;i < x.length;i++) { Syst ...