select count(*),(floor(rand(0)*2))x from information_schema.tables group by x
rand()是用来产生随机数的,他的范围是[0,1]之间
可以看到每一次的数值都是不同的
而且rand()有一个BUG,报错也就是利用了这个BUG,这个后面会细说,暂时就理解产生随机数就好了
floor()是用来取整的,重点:没有四舍五入
这样一来用floor()来包裹着rand()岂不是没有用了吗?因为结果一定会是0。
所以我们要对rand()进行处理,那就是对他乘个2,这样就会出现1。
这样子我们会有个疑问,因为是rand()是随机的所以出现0,1也是随机的,是不可控的,可以看到确实是随机的
所以我们要对rand()做一点点修改,将rand()加个0,变成rand(0)
加一个0以后的我们发现数字的结果就唯一,是01101,这串数字特别的重要,我们的报错就是来自于他重要的事情说三遍:01101,01101,01101
为了演示方便我创建了一个test表
接下来我们用一下group by 看看有什么作用
select * from test group by age;
可以看到 group by 创建了一个新的虚空的表,并且以 by 后的字段 age 来查询test表,如果重复了就不再添加,而且新的虚拟表的主键是 by 后面的字段,这就是上图中的 age 。(ps:一个表中主键是不能重复的)
count()函数允许对表中符合特定条件数的所有行进行计数,举个例子
select count(*) from test group by age;
用count(),可以清楚的知道表中,几个15岁,几个18岁,几个19和20岁。
我们来看这个语句
select count(*) from test group by floor(rand(0)*2);
会注意到是不是和文章开始的报错语句不太一样,其实文章开始的那个报错语句是这个的变形而已,为了更直观咱们就看这个。
还记得我之前说的 rand() 的一个小BUG吗,那就是
group by floor(rand(0)*2)
在执行是会计算一次 floor(rand(0)*2)
,但是在插入数据时还会执行一次floor(rand(0)*2)
。floor(rand(0)*2)
的前5个计算结果为 01101语句开始运行
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.