MYSQL双查询错误2
一、关键点
MYSQL双查询错误之所以产生,有两个关键点:
(1)SQL语句中使用GROUP BY语句时会生成临时表;
(2)RAND()在查询和存储时生成的随机数有可能不同.
补充:===========================================================================================
(1)group by常和min(),max(),count(),sum(),avg()等聚合函数配合使用,如: select name, count(*) from user group by name; 这条语句会根据user表中的name字段进行分组,name字段中的值一样的记录会被划分为同一个组,而count(*)则会计算每个组中
的记录数,当我们运行上述语句时,会得到: +--------+------------+
| name | count(*) |
+--------+------------+
| andy | 1 |
| nana | 1 |
| mike | 1 |
+--------+------------+ user表中的name字段有三个值:mike、anna、andy,所以执行SQL语句后会得到三个分组,每个分组的记录数为1;我们往user
表中再添加一条记录: insert into user value(4,'mike'); 此时表中有两个同名的人,都叫mike,再执行SQL语句时,会得到如下结果: +--------+------------+
| name | count(*) |
+--------+------------+
| andy | 1 |
| nana | 1 |
| mike | 2 |
+--------+------------+
在name字段值为mike的分组中,可以看到记录数变为2了. (2)rand()函数随机生成0和1之间的数(0和1除外)
================================================================================================
二、原理探究
1. 回顾在《MYSQL双查询错误1》中搭建的环境中使用的产生错误信息的SQL语句:
select count(*), concat((select database()), floor(rand()*2)) as a from user group by a; 说明:SQL语句中,rand()*2随机产生0和2之间(不包括0和2)的随机数,而floor(rand()*2)则会随机产生0和1这两个数之一
执行上述语句时,在会话期间,数据库会为当前会话维护一个主键为a的临时表,各个字段如下:
a count(*) 补充说明:实际上,字段的顺序应该是 count(*) a 只是我们把主键a放到了前面
针对由我们的SQL语句产生的每一个a值,数据库首先会查询临时表中有没有一样的a字段值,若有,则相应的count(*)字
段值加1,否则的话将a值插入到临时表的a字段中,相应的count(*)字段值置为1. 这里,我们先来理一理两个问题:
(1)针对新产生的a值,临时表中有相同的a字段值时是否会发生查询错误?
(2)针对新产生的a值,临时表中没相同的a字段值时是否会发生查询错误?
先来解决第一个问题. 将SQL语句修改如下:
select count(*), concat((select database()), 1) as a from user group by a; 变化:针对user表中的每一条记录,SQL语句产生的a值均为injection1
多次执行新的SQL语句,情况是:没有查询错误产生,每次都得到的结果如下:

为什么不会产生查询错误呢?理解在这期间临时表中存储的信息如何变化就明白了. 首先,刚开始临时表中没有数据.

针对user表中的第一条记录,新的SQL语句产生的a值为injection1,数据库在临时表中查询到没有任何a字段值与刚产生
的a值相同,于是决定:将a值插入到a字段中.

针对user表的第二条记录,新的SQL语句产生的a值同样为injection1,数据库在临时表中查询到已经有a字段值和新的
a值一样了,于是决定:将对应的count(*)字段值加1.

user表中有4条记录,最终临时表中的count(*)字段会变为4. 可以看到,在新的SQL语句执行的会话期间,没有查询错误产生.
情况逐渐明了了,查询错误的产生可能与第一个问题无关. 接下来我们讨论第二个问题.
2. 为了便于叙述,将新的SQL语句称为SQL2,而另一个称为SQL1吧. 执行SQL1,当产生的a值与临时表中的各个a字段值不
同时(查询阶段),会将新的a值插入临时表中的a字段(插入阶段),并将对应的count(*)字段值置为1. 在这个过程中,错误
就发生在插入阶段. 在这个过程中,有两个关键的时点,一是查询时,二时插入时,在这两个时间点,rand()都会产生随机数,
随机数的产生使得两个时间点的a值有可能不一致,导致在插入阶段可能发生错误. 简单点说就是:在查询阶段和插入阶段的a
值有可能不同,这就是造成错误的原因. 接下来,我们从1条记录开始慢慢增加user表中的记录数,每一次都执行SQL1,看看
会发生什么.
(1)记录数为1时(只保留id为1的记录)

情况是:无论执行多少次,都不会发生错误,为什么呢?首先临时表是空的,而user表中只有一条记录,对应
要统计到临时表中的a值也只有一个,错误自然不会发生.
(2)记录数为2时(增加id为2,name为anna的记录)

可以看到,第三次执行SQL1的时候,发生了错误. 第一次为什么没错误呢?发生的情况是这样的.
i)针对user表中的第一条记录,产生的a值为injection0,查询阶段的a值就是injection0,查询到临时表中a字段值没有 injection0,于是数据库决定:插入a值. 前面我们说过,由于随机数的关系,在插入阶段a值有可能会变化,有可能变为 injection1,或者还是injection0.不过这时候由于临时表是空的,所以都不会有错误发生,在插入阶段a为injection0时, 就插入injection0,否则插入injection1.显然,这里最终插入的是injection0. ii)针对user表中第二条记录,产生的a值必然是injection0,在查询阶段数据库检查到临时表中a字段值中有injection0, 于是数据库决定:将对应的count(*)字段值加1. 这时候,不论在插入阶段a值变为什么,数据库都不会再执行检查,而是直 接将count(*)字段值加1. 这就是第一次执行SQL1时发生的情况. 那么对于第一次执行SQL1,在什么情况下会发生错误呢? iii)在ii中,针对user表中第二条记录产生的a值若为injection1,在查询阶段数据库检查到临时表中所有a字段值中没有 injection1,于是决定:将injection1插入到临时表中. 不巧,在插入阶段,a值变为了injection0,由于表中本来就存 在injection0了,于是就会报错了. 同样可以这样去分析第二次第三次执行SQL1发生的情况.
(3)记录数为3及以上,都会引发错误.
3. 经过以上分析,我们已经抓到了错误最关键的点了:在查询阶段和插入阶段的主键值不一致导致了错误的产生.
三、结论
在将数据统计到临时表中时(查询阶段和插入阶段的数据不一致),由于主键具有唯一性,如果某主键值在临时表中已经存
在,而在插入阶段还试图插入相同的主键值时,数据库便会报错.
============================ 参考 ==============================
https://www.cnblogs.com/laoxiajiadeyun/p/10278512.html
MYSQL双查询错误2的更多相关文章
- MYSQL双查询错误1
一.基础知识 开始讲解MYSQL双查询错误之前,我们先了解一下双查询语句以及需要使用到的几个数据库函数和GROUP BY语句 1. 双查询语句 先了解一下什么是子查询,子查询就是嵌入第一层select ...
- MySQL 子查询(三) 派生表、子查询错误
From MySQL 5.7 ref:13.2.10.8 Derived Tables 八.派生表 派生表是一个表达式,用于在一个查询的FROM子句的范围内生成表. 例如,在一个SELECT查询的FR ...
- 利用Keepalived+mysql构建高可用MySQL双主自动切转
转载:http://www.it300.com/index.php/article-15266.html 关于MySQL-HA,目前有多种解决方案,比如heartbeat.drbd.mmm.共享存储, ...
- sql注入--双查询报错注入原理探索
目录 双查询报错注入原理探索 part 1 场景复现 part 2 形成原因 part 3 报错原理 part 4 探索小结 双查询报错注入原理探索 上一篇讲了双查询报错查询注入,后又参考了一些博客, ...
- sql注入--双查询报错注入
sql注入--双查询报错注入 背景:在sqli-labs第五关时,即使sql语句构造成功页面也没有回显出我们需要的信息,看到了有使用双查询操作造成报错的方式获得数据库信息,于是研究了一下双查询的报错原 ...
- Mysql双主加Keepalived+读写分离
一.MySQL于keepalived简介** 前言: 在企业中,数据库高可用一直是企业的重中之重,中小企业很多都是使用mysql主从方案,一主多从,读写分离等,但是单主存在单点故障,从库切换成主库需要 ...
- 使用Keepalived实现MySQL双主高可用
MySQL双主配置 环境准备: OS: CentOS7 master:192.168.1.10 backup:192.168.1.20 VIP:192.168.1.30 一.安装MySQL数据库. 在 ...
- 基于keepalived搭建mysql双主高可用
目录 概述 环境准备 keepalived搭建 mysql搭建 mysql双主搭建 mysql双主高可用搭建 概述 传统(不借助中间件)的数据库主从搭建,如果主节点挂掉了,从节点只能读取无法写入,只能 ...
- 一个月后,我们又从 MySQL 双主切换成了主 - 从!
这是悟空的第 157 篇原创文章 官网:www.passjava.cn 你好,我是悟空. 一.遇到的坑 一个月前,我们在测试环境部署了一套 MySQL 高可用架构,也就是 MySQL 双主 + Kee ...
随机推荐
- dojo - 相关教程
https://blog.csdn.net/dojotoolkit/article/details/6688058
- Oracle VM VirtualBox - ping不通虚拟机
问题描述 用Oracle VM VirtualBox创建虚拟机后,本机电脑ping不通虚拟机 解决方案 https://www.cnblogs.com/ranrongzhen/p/6958485.ht ...
- Mybatis-对数据库的关联查询
由于MyBatis逆向工程生成的代码只能进行对单表的操作(功能已经很强大了),但是远远不能满足开发的需要,下面就简单讲解下MyBatis进行关联查询的简单案例. 一.一对一关联查询 1 ...
- python 数组array的一些操作
对一些特定大小的元素进行操作 1.将数组Arr中大于100的值都设定为100 Arr[Arr >100] = 100 利用array索引的内置 numpy.minimum(Arr, 100 ...
- sql注入的原理是什么,怎么预防sql注入
为什么会产生sql注入: 主要原因,对用户输入的绝对信任,相信所有用户的输入都是可信的,没有对用户输入的语句进行过滤或者筛选,直接放到sql语句中进行拼接,从而导致了sql注入的产生 例如: < ...
- 2.11 webdriver中使用 FileUtils ()
http://snkcxy.iteye.com/blog/1845862 ex: 比较网页截图图片与预期是否一致 File screenshot=((TakesScreenshot)driver ). ...
- 为什么hadoop中用到的序列化不是java的serilaziable接口去序列化而是使用Writable序列化框架
继上一个模块之后,此次分析的内容是来到了Hadoop IO相关的模块了,IO系统的模块可谓是一个比较大的模块,在Hadoop Common中的io,主要包括2个大的子模块构成,1个是以Writable ...
- vue 实现上一周、下一周切换功能
效果图: html 显示部分: js 显示部分: preNextBtn(val){ let _this = this; this.tableList = []; //数据重置为空 _this.show ...
- Django框架-模板层
Django框架-模板层 一.模板语法传值 1.验证是否python所有的数据类型都可以传递到前端 locals()的妙用:该方法虽然好用,但是在某些情况下会造成资源的浪费 结论:整型.浮点型.字符串 ...
- 吴裕雄 python 机器学习——集成学习AdaBoost算法回归模型
import numpy as np import matplotlib.pyplot as plt from sklearn import datasets,ensemble from sklear ...