MYSQL报错语句很多,但是了解其原理才是做重要的
让我们先看一段报错语句
代码语言:javascript复制select count(*),(floor(rand(0)*2))x from information_schema.tables group by x
这条报错的语句最重要的部分有三个:
- rand(0)
- floor()
- group by
现在我就讲一下这三个部分
一、rand(0)
rand()是用来产生随机数的,他的范围是[0,1]之间
可以看到每一次的数值都是不同的
而且rand()有一个BUG,报错也就是利用了这个BUG,这个后面会细说,暂时就理解产生随机数就好了
二、floor()
floor()是用来取整的,重点:没有四舍五入
这样一来用floor()来包裹着rand()岂不是没有用了吗?因为结果一定会是0。
所以我们要对rand()进行处理,那就是对他乘个2,这样就会出现1。
这样子我们会有个疑问,因为是rand()是随机的所以出现0,1也是随机的,是不可控的,可以看到确实是随机的
所以我们要对rand()做一点点修改,将rand()加个0,变成rand(0)
加一个0以后的我们发现数字的结果就唯一,是01101,这串数字特别的重要,我们的报错就是来自于他重要的事情说三遍:01101,01101,01101
三、group by
为了演示方便我创建了一个test表
接下来我们用一下group by 看看有什么作用
代码语言:javascript复制select * from test group by age;
可以看到 group by 创建了一个新的虚空的表,并且以 by 后的字段 age 来查询test表,如果重复了就不再添加,而且新的虚拟表的主键是 by 后面的字段,这就是上图中的 age 。(ps:一个表中主键是不能重复的)
现在就要进入重点了,在此之前我再介绍一下count()
count()函数允许对表中符合特定条件数的所有行进行计数,举个例子
代码语言:javascript复制select count(*) from test group by age;
用count(),可以清楚的知道表中,几个15岁,几个18岁,几个19和20岁。
OK,现在我们开始
我们来看这个语句
代码语言:javascript复制 select count(*) from test group by floor(rand(0)*2);
会注意到是不是和文章开始的报错语句不太一样,其实文章开始的那个报错语句是这个的变形而已,为了更直观咱们就看这个。
还记得我之前说的 rand() 的一个小BUG吗,那就是
就是查询的时候如果使用rand()的话,该值会被计算多次,这个是MySql官方说的,这个“多次计算”在咱的报错语句中来解读就是,group by floor(rand(0)*2)
在执行是会计算一次 floor(rand(0)*2)
,但是在插入数据时还会执行一次floor(rand(0)*2)
。
还有一个,我在提醒一次 floor(rand(0)*2)
的前5个计算结果为 01101
语句开始运行
- group by 以
floor(rand(0)*2)
为主键,建立一个虚拟的空表
| key | count |
| ---- | ----- |
| | |
- 查询第一条记录,计算
floor(rand(0)*2)
(这是第一次计算),得到的值是0,查看表发现没有0,所以进行插入操作,但是在插入时又会计算一次floor(rand(0)*2)
(这是第二次计算)结果为1,此时的表为 | key | count |
| ---- | ----- |
| 1 | 1 |
- 查询第二条记录,计算
floor(rand(0)*2)
(这是第三次计算),得到值是1,查看表发现已经有了,则不进行插入操作,直接count 1,此时表为 | key | count |
| ---- | ----- |
| 1 | 2 |
- 查询滴三条记录,计算
floor(rand(0)*2)
(这是第四次计算),得到的是0,查看表发现没有,进行插入操作,但是在插入之前会在此计算floor(rand(0)*2)
(这是第五次计算),得到的是1。然而表中已经有key为1 ,所以会产生报错。 | key | count |
| -------- | ----- |
| 1 | 2 |
| 1 (出错) | |
总结
出现报错的原因是,因为已经要执行插入数据操作,才发现了主键冲突。
这个报错是利用 rand() 的特殊性,以及floor(rand(0)*2)
前5个数字的可预知性,以及可以通过 group by 来创建一个空的虚拟表,这些条件综合在一起产生的报错。
Q.E.D.