Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ORDER BY random() with seed in SQLITE

Tags:

I would like to implement paging for a random set

Select * from Animals ORDER BY random(SEED) LIMIT 100 OFFSET 50  

I tried to set int to some integer and to some fracture. Doesn't work

How do I seed random in sqlite?

I am tacking a chance here with down votes because similar question already exist - Seeding SQLite RANDOM(). I just didn't get the php solution.

like image 440
Luda Avatar asked Jun 17 '14 05:06

Luda


People also ask

How random is random seed?

In this case, random is actually pseudo-random. Given a seed, it will generate numbers with an equal distribution. But with the same seed, it will generate the same number sequence every time. If you want it to change, you'll have to change your seed.

How do I select a random row in SQLite?

select * from foo where rowid = (abs(random()) % (select (select max(rowid) from foo)+1));

How to generate random number in SQLite?

In SQLite, you can use the random() function to generate a pseudo-random number. This is great, but the value returned is between -9223372036854775808 and +9223372036854775807.


2 Answers

Short answer:

You can't. SQLite's random() function does not support a seed value.

Not so short answer:

Checking SQLite's func.c shows that random() is defined without any parameters..

VFUNCTION(random,            0, 0, 0, randomFunc       ),

..and this randomFunc() just calls sqlite3_randomness() (again without any explicit seed value) to obtain a random value of sizeof(sqlite_int64) bytes.

Internally, the implementation of sqlite3_randomness() (see random.c) will set up the RC4 pseudo-random number generator the first time it is used with random seed values obtained from the OS:

  /* Initialize the state of the random number generator once,
  ** the first time this routine is called.  The seed value does
  ** not need to contain a lot of randomness since we are not
  ** trying to do secure encryption or anything like that...
  **
  ** [..]
  */
  if( !wsdPrng.isInit ){
      [..]
      sqlite3OsRandomness(sqlite3_vfs_find(0), 256, k);
      [..]
      wsdPrng.isInit = 1;
  }

Actually, SQLite's unit test functions themselves just use memcpy() on the global sqlite3Prng struct to save or restore the state of the PRNG during test runs.

So, unless you're willing to do something weird (like create a temporary table of consecutive numbers (1..max(Animals)), shuffle those around and use them to select 'random-seeded' RowIds from your Animals table) I suppose you're out of luck.

like image 98
mvanallen Avatar answered Oct 15 '22 10:10

mvanallen


I would not usually copy an existing answer, but I can see that you have left a comment asking the author of this answer to explain how it works already a few weeks ago and no explanation has been given. I will therefore copy the relevant part and try to explain whats going on. If this explanation is good, do go and vote on the original answer.

$seed = md5(mt_rand());
$prng = ('0.' . str_replace(array('0', 'a', 'b', 'c', 'd', 'e', 'f'), array('7', '3', '1', '5', '9', '8', '4'), $seed )) * 1;
$query = 'SELECT id, name FROM table ORDER BY (substr(id * ' . $prng . ', length(id) + 2)';

The first two rows are just about creating a seed of a sort. The result is a decimal number with lots of decimals like:

0.54534238371923827955579364758491

Then the sql select uses this number to multiply with the numeric row id of every row in the SQLite table. And then the rows are sorted according to the decimal part of the resulting product. Using fewer decimals, the sort order would look something like this:

row id   row id * seed      sort order
1        0.545342384        545342384
2        1.090684767        090684767
3        1.636027151        636027151
4        2.181369535        181369535
5        2.726711919        726711919
6        3.272054302        272054302
7        3.817396686        817396686
8        4.362739070        362739070

After sorting this would be the result:

row id   row id * seed      sort order
2        1.090684767        090684767
4        2.181369535        181369535
6        3.272054302        272054302
8        4.362739070        362739070
1        0.545342384        545342384
3        1.636027151        636027151
5        2.726711919        726711919
7        3.817396686        817396686

In this sample I used only eight rows so the result is not very random looking. With more rows the result will appear more random.

This solution will give you the same order repeatedly as long as:

  • You use the same seed
  • No new rows have appeared in the table and no rows have been deleted from the table
like image 28
user1429080 Avatar answered Oct 15 '22 11:10

user1429080