Contents

需求:需要从数据库的m条记录中随机筛选出样本n,使用sql语句。
分析:分两种
【a】. 定比抽取,以一定的概率p来筛选;
【b】. 定量抽取,筛选出一定的个数k。
对于【a】,每次执行sql的时候,所得到的记录集个数是不同的;对于【b】,每次得到的记录集个数相同。在实现【b】的时候,一般将频率近似成概率之后计算,及转换成【a】的样式,如果最终得到的记录个数小于n,则需要重复运行。

对于【b】,网上有一种最简单的方式:

[SqlServer]:select top 10 * from tableName order by newid();
[Mysql]:select 10 from tableName order by rand() limit 10;

但是从语句上来看,此类抽取方法消耗时间比较大,一者,是对每一行记录产生随机数;二者,是对这些随机数进行排序。我在mysql上使用300w+的数据,用时40s+。如果网站需要筛选一定的记录,显然不适合此种方法。

网上给出了另外一种方法,针对mysql,代码如下:

select  from tableName where id>=(select floor(rand()((select max(id) from
tableName)-(select min(id) from tableName))+(select min(id) from
tableName))) order by id limit 10;

这种方法乍一测试是可以完成取样效果,而且用时比较短,但是总觉得有什么的地方不对劲。我希望能够用数学证明。不过如今脑袋已然转不动,细节上无法完成证明,只是能够用极端值,来否定上述的方法。
定义问题及方法如下:数据库总记录为m,第i个记录的值为xi,最大值为max,最小值为min,先从中筛选,筛选概率p(x[i]>=(rand()(max-min)+min));从中筛选出n条记录(n<m)之后,将其按照值排序,然后取前面的k个值作为最终的随机选择结果。
用这种方法,对于值均匀分布的记录而言,第一阶段的概率是可以估计的,每个记录被选中的概率为i/m(几何概型),第一步被选中之后,第二步被选中的概率为ii/n
k,其中ii为n确定后的记录次序,是均匀分布的,但不是随机的,ii的值依赖于第一步的结果,理论上每一个记录被选中的概率为(i/m)(ii/nk),但是ii的值我想不到办法确定,所以最终没有办法确然,均匀分布下得到的结果是否是随机的。
但是对于不均与的值而言,可以举出一些极端值来否定上文代码。考虑共有4条记录,id值分别为(1,98,99,100),我希望从中选择两条记录,可以发现第一轮结束之后,入选可行性最大的是值:98,99,100,经过第二轮,最有可能的结果是98,99;第一条记录入选最终的可能性远远小于1/2;所以可以推断出,上述的sql代码肯定是有问题的。
—-结果有点悲催,我没有想到比较高效的从m条记录中随机筛选出n条记录的方法。所以实践的时候,只能定比抽取。代码如下:

[mysql]:select * from tableName where rand()>(1-ratio);

以上。

Contents