给sqlite中的random()设置seed

0x01

As we know, 在sqlite中random函数是不能有参数的, 也就是说不能设置seed, 每次调用都是返回一个随机的介于-9223372036854775808 and +9223372036854775807之间的整数.如:

select random();
select * from table_name order by random();

上面的语句可能经常用,但是random()返回的是一个随机整数, 大家有没有想过为什么 order by 一个整数, 就可以产生随机顺序呢, 如果我自己设置一个整数, 结果是什么样呢, 比如:

select * from table_name order by -5089225945763057372;

发现结果跟没排序没什么两样, order by后面一般都是跟列名阿, 这就涉及到在sqlite中源码的实现了,找个时间看下源码, 这里就不深入讨论了,好像是生成一个伪列来实现的, 下面讨论一种自定义的简单随机方法.

0x02

那么如果我们需要一个seed, 每次如果seed一样,返回的数据集顺序需要也完全一样怎么办呢, 在stackOverflow中发现了一个php的解决方案,而且解释的也很详细, 可以看下这个:order by random with seed in sqlite.
在这里给出Android中的实现.

0x03

分为三部

  1. 生成seed(形如 0.54534238371923827955579364758491),一般计算一个字符串的MD5值,在转换成十六进制字符串, 替换掉里面的[A,F]区间内的数就可以了;
  2. 使用 ID * seed, 并取小数位
  3. 使用这个值排序

计算结果如下表所示:

row_idid * seedsort order
10.545342384545342384
21.090684767090684767
31.636027151636027151
42.181369535181369535
52.726711919726711919
63.272054302272054302
73.817396686817396686
84.362739070362739070

经过按照sort order排序之后, 就成这样子啦,

row_idid * seedsort order
21.090684767090684767
42.181369535181369535
63.272054302272054302
84.362739070362739070
10.545342384545342384
31.636027151636027151
52.726711919726711919
73.817396686817396686

不过这里数据有点少, 数据量比较大的时候, 就比较随机了,如下:

sqlite> select _id from points order by substr(_id * 0.5453423837192, length(_id) + 2) limit 10;
3242
4863
6484
1621
805
2426
4047
5668
7289
161

代码如下:

String seed = "hello world";
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] seedMd5 = md.digest(seed.getBytes());
String seedHex = bytesToHex(seedMd5);
String finalSeed = "0." + num.replace('0','7').replace('a', '3').replace('b', '1').replace('c', '5').replace('d', '9').replace('e', '8').replace('f', '4');
String query = "select * from table_name order by substr(id * " + finalSeed + ",length(id) + 2);";
Cursor cursor = database.rawQuery(query);

public static String byteArrayToHex(byte[] byteArray) {
        // 首先初始化一个字符数组,用来存放每个16进制字符
        char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

        // new一个字符数组,这个就是用来组成结果字符串的(解释一下:一个byte是八位二进制,也就是2位十六进制字符(2的8次方等于16的2次方))
        char[] resultCharArray = new char[byteArray.length * 2];
        // 遍历字节数组,通过位运算(位运算效率高),转换成字符放到字符数组中去
        int index = 0;

        for (byte b : byteArray) {
            resultCharArray[index++] = hexDigits[b >>> 4 & 0xf];
            resultCharArray[index++] = hexDigits[b & 0xf];
        }
        return new String(resultCharArray);
    }
    原文作者:张老虎
    原文地址: https://www.jianshu.com/p/5a601291ec96
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞