0x1 须要用到的MySQL函数html
count():用来统计表中或数组中记录的一个函数 count(*)某表中全部的列mysql
floor():返回小于等于该值的最大整数【向下取整】
sql
rand(): 产生随机数
数据库
0x2原理分析数组
接下来咱们再分析其报错的造成 缘由:函数
先谈group by 函数:spa
在表中再插入两条数据,name值都为“bbb”:3d
mysql> INSERT INTO test VALUES("3","bbb"); mysql> INSERT INTO test VALUES("4","bbb");
成功后表以下:code
这时候咱们使用group by 语句时,MySQL会将查询结果分类汇总,重复的内容会合并为一项:htm
mysql> SELECT name FROM test GROUP BY name;
这时候再使用count()函数就能够对不一样的条目计数:
mysql> SELECT count(*),name FROM test GROUP BY name;
如图:aaa有一条,bbb有3条
其背后的实现原理以下:
在执行group by name语句时,MySQL会在内部创建一个虚拟表,用来储存列的数据,表中会有一个group_key值做为表的主键,这里的主键就是用来分类的name列中获取,当查询数据时,取数据库数据,而后查看虚拟表中存在不,不存在则插入新记录
当读取到第一行数据时,aaa不存在,将aaa放入主键列中,1放在id列中
而后继续往下走,到了bbb,不存在,也放进去
往下执行,遇到多余的bbb,已经有bbb存在,就汇总在一块儿,内部状况以下:
以下,最后在查询的时候根据group by内部的实现方式返回分类后的结果:
当咱们加上count(*)函数时,操做过程为:查看虚拟表是否存在该主键值,不存在则插入新记录,存在则count(*)字段直接加1
这样就能对上面的分类结果进行统计,而后将统计结果返回:
因此双查询报错的关键就在这里,主要的缘由在于rand()函数在group by的过程当中被触发了屡次,
让咱们回看一下构造的报错语句:
mysql> SELECT count(*),concat((SELECT database()),"~",floor(rand()*2))as a FROM test GROUP BY a;
执行前虚拟表为空:
当第一次执行时,group by 分组,其取的数据的是以a为别名的这条语句,假设这时的concat((SELECT database()),"~",floor(rand()*2))
生成结果为sql_test~0
,group就以sql_test~0
查询虚拟表,发现表中没有该值的主键,因而将这条语句的结果插入到虚拟表中。
注意!是将这条语句的结果插入到虚拟表中,而不是将 sql_test~0
插入到虚拟表中,以下:
(将concat((SELECT database()),"~",floor(rand()*2))
以a为别名,方便做图)
因为虚拟表没有内容,因此会将其插入到虚拟表中,这里的插入过程当中,因为插入的是a语句的结果,因此在插入时a语句中的rand()函数会再次执行,即插入的值可能为sql_test~0
也可能为 sql_test~1
,这里假设插入时a执行的结果为sql_test~0
:
因此上面的状况就是用sql_test~1
这个结果查询虚拟表,不存在该数据,因而插入虚拟表,插入时又运算一次,而后插入的值变成了sql_test~0
,因此这就是主要的冲突,表中只有一条数据还好,即便查询虚拟表的值和插入虚拟表的值不是同一个,但虚拟表也只生成一条记录,不会出现问题。
然而当表的数据出现两条以上的时候,第group by 在处理完第一条数据后会往下继续处理第二条,因而第二条还会按第一条的处理方式进行:
因而就会报错,报错内容以下:
ERROR 1062 (23000): Duplicate entry 'sql_test~0' for key 'group_key'
若是第二次查询和插入的结果都一致:就会有下面两种状况:
都是sql_test~0
:表里已存在,该主键的count(*)值+1
都是sql_test~1
:表里没有,插入造成新的主键
部份内容转载:https://www.cnblogs.com/laoxiajiadeyun/p/10283251.html