用MySQL实现组内随机采样

 典型的MySQL实现随机采样是这样的:http://blog.csdn.net/frank_monkey_lee/article/details/53303622

 就是0.01s不到的那条SQL,这样的SQL针对的id属性是主键(有索引,查询起来很快)并且用的join,所以跑起来很快,但是想加上分组限定,那就要涉及其它属性(就算在其它属性上加上索引,无论在速度还是实现随机抽样的逻辑上都不行,前者无论有没有索引都差不多,后者逻辑很难实现,辅助索引和二级索引我不懂~),总之加上其它属性的约束条件之后本来不到0.01s的高速慢成了5s,绝对不行,得想想办法。又做了一下尝试,对主键加上范围限定,也高速。那么自然而然就想到将主键id和组的概念结合起来,也就是说通过id能得到该记录所属哪个组的信息。其实很简单,我的id最多8位,12个组,那么新加一列,类型为10位的int类型即可,也就是说最高的前两位表示组,例如第一组的筛选条件就是id>=100000000 and id<2000000000 但是组内随机采样这条SQL有个硬性的数据分布的要求,那就是同组的数据必须放在一起并且id是连续的,否则会出现两种情况:

SELECT ROUND(RAND() * (SELECT MAX(groupId)-200000000 FROM `2008Index` 
where groupId >= 200000000 and groupId < 300000000)+200000000);

   ② SELECT t1.*
FROM `2008Index` AS t1 JOIN 
(SELECT ROUND(RAND() * (SELECT MAX(groupId)-200000000 FROM `2008Index` 
where groupId >= 200000000 and groupId < 300000000)+200000000) AS groupId) AS t2
WHERE t1.groupId >= t2.groupId 
ORDER BY t1.groupId ASC LIMIT 1;

1.如果组2的数据是连续分布的也就是id是连续的,那么第一条SQL可以实现组内随机采样,但如果200000000+的数据中的id是不连续的,例如2000010000下一条直接是2000030000,这样第一条SQL就不能直接用,因为它的原理是id最大值乘一个小于1的小数,也就是得到一个[0,max(id)]范围的一个数,可是如果数据不连续,那这个随机数很有可能恰好落在那个gap里,也就是这条记录,根本不存在。

2.那么好吧,那就把第一条SQL放到第二条里,第二条SQL的原理也就是网上最常见的随机采样的SQL,是选择大于这个随机数的最小的id对应的记录,那么也是如果只要随机数在gap里,那么得到的记录就一直是gap后的那条记录,所以实验基本上采的样就是那么几个数据段的最小记录来回出现,故也不行。

所以必须进行数据的整理,将同组数据放在一起,导入数据库主键自增,然后新增一列,将主键和类的语义连接起来。


故总结起来,要实现用MySQL实现组内随机采样,第一将主键和类的语义连接起来,第二数据分布有要求。



遇到的小问题:

1.MySQL属性类型int(11),不是11位数的意思…是显示长度,int类型的最大值就是2的31次方-1,和括号里的参数没关系,那个参数只是给人看的时候显示多少位,所以位数多就用bigint类型

2.主键设计原则,http://blog.csdn.net/linzhiqiang0316/article/details/52881151,其中的主键无意义原则挺好,但是为了做实验,就不管它了。

3.用MySQL workbench建表时,UQ、BIN、ZF等标识的意思

http://blog.csdn.net/fdipzone/article/details/45035483

4.将一个列的数据复制到另一列,update test set col1=col2;但是如果一个没有设置not null一个设置了,这条语句就报错了,就不能用了。用到这个是想把新增的列改为主键,但是新建列的时候忘记设置not null了,所以报错,修改为not null也不行,新建一个not null的列再复制过去,前文说了,也不行。乖乖重新导入一遍数据吧。










猜你喜欢

转载自blog.csdn.net/xerjava/article/details/78332680