MYSQL报错语句很多,但是了解其原理才是做重要的

让我们先看一段报错语句

select count(*),(floor(rand(0)*2))x from information_schema.tables group by x

这条报错的语句最重要的部分有三个:

  1. rand(0)
  2. floor()
  3. group by

现在我就讲一下这三个部分

一、rand(0)

rand()是用来产生随机数的,他的范围是[0,1]之间

image-20200725173941167

可以看到每一次的数值都是不同的

而且rand()有一个BUG,报错也就是利用了这个BUG,这个后面会细说,暂时就理解产生随机数就好了

二、floor()

floor()是用来取整的,重点:没有四舍五入

image-20200725174546195

这样一来用floor()来包裹着rand()岂不是没有用了吗?因为结果一定会是0。

所以我们要对rand()进行处理,那就是对他乘个2,这样就会出现1。

image-20200725185235658

这样子我们会有个疑问,因为是rand()是随机的所以出现0,1也是随机的,是不可控的,可以看到确实是随机的

image-20200725185520120

所以我们要对rand()做一点点修改,将rand()加个0,变成rand(0)

image-20200725185728527

加一个0以后的我们发现数字的结果就唯一,是01101,这串数字特别的重要,我们的报错就是来自于他重要的事情说三遍:011010110101101

三、group by

为了演示方便我创建了一个test表

image-20200725191514263

接下来我们用一下group by 看看有什么作用

select * from test group by age;

image-20200725191830700

可以看到 group by 创建了一个新的虚空的表,并且以 by 后的字段 age 来查询test表,如果重复了就不再添加,而且新的虚拟表的主键是 by 后面的字段,这就是上图中的 age 。(ps:一个表中主键是不能重复的)

现在就要进入重点了,在此之前我再介绍一下count()

count()函数允许对表中符合特定条件数的所有行进行计数,举个例子

select count(*) from test group by age;

image-20200725202153725

image-20200725202237338

用count(),可以清楚的知道表中,几个15岁,几个18岁,几个19和20岁。

OK,现在我们开始

我们来看这个语句

 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

语句开始运行

  1. group by 以 floor(rand(0)*2) 为主键,建立一个虚拟的空表

    keycount
  2. 查询第一条记录,计算 floor(rand(0)*2)(这是第一次计算),得到的值是0,查看表发现没有0,所以进行插入操作,但是在插入时又会计算一次floor(rand(0)*2) (这是第二次计算)结果为1,此时的表为

    keycount
    11
  3. 查询第二条记录,计算floor(rand(0)*2)(这是第三次计算),得到值是1,查看表发现已经有了,则不进行插入操作,直接count+1,此时表为

    keycount
    12
  4. 查询滴三条记录,计算floor(rand(0)*2)(这是第四次计算),得到的是0,查看表发现没有,进行插入操作,但是在插入之前会在此计算floor(rand(0)*2)(这是第五次计算),得到的是1。然而表中已经有key为1 ,所以会产生报错。

    keycount
    12
    1 (出错)

总结

出现报错的原因是,因为已经要执行插入数据操作,才发现了主键冲突。

这个报错是利用 rand() 的特殊性,以及floor(rand(0)*2)前5个数字的可预知性,以及可以通过 group by 来创建一个空的虚拟表,这些条件综合在一起产生的报错。

Q.E.D.