PHP MySQL pagination with random ordering
Random ordering in MySQL is as sticky a problem as they come. In the past, I've usually chosen to go around the problem whenever possible. Typically, a user won't ever come back to a set of pages like this more than once or twice. So this gives you the opportunity to avoid all of the various disgusting implementations of random order in favor of a couple simple, but not quite 100% random solutions.
Solution 1
Pick from a number of existing columns that already indexed for being sorted on. This can include created on, modified timestamps, or any other column you may sort by. When a user first comes to the site, have these handy in an array, pick one at random, and then randomly pick ASC
or DESC
.
In your case, every time a user comes back to page 1, pick something new, store it in session. Every subsequent page, you can use that sort to generate a consistent set of paging.
Solution 2
You could have an additional column that stores a random number for sorting. It should be indexed, obviously. Periodically, run the following query;
This may not work for your specs, as you seem to require every user to see something different every time they hit page 1.UPDATE table SET rand_col = RAND();
MySQL Pagination with Randomness
- Filter 1M rows to find 100K rows -- a full table scan of 1M rows
- Pick, say, 10 random rows from the 100K -- a scan of 100K rows, unless you use some technique from http://mysql.rjweb.org/doc.php/random .
- Leave behind something that lets you "paginate". Do you care if it accidentally repeats rows? Is this web-based, meaning that "leaving something behind" can be messy -- such as listing all the items already seen when in the url of the [Next] button.
- Pagination via LIMIT and OFFSET is Order(N*N) to walk through all N pages. 100K rows, 10 rows at a time, would be something like 500M. Even worse than the original 1M.
- Prep: Add an extra column with some random number in it. Index it. This will become both the randomizer and the "where you left off" to avoid using the costly
OFFSET
. - See the link for one approach to getting the first 10 random rows.
- See http://mysql.rjweb.org/doc.php/pagination for more on "remembering where you left off".
LIMIT 10
. (My gut says that won't be practical.)Summing up:
SELECT ...
FROM ...
WHERE filters...
AND rnd > $left_off
ORDER BY rnd
LIMIT 10;
If you start $left_off
at 0, you get a repeatable, but seemingly random, sequence. And the [Next] button only needs to pass the last value of rnd
that was fetched from the current page.Now to make it not repeatable? Re-populate rnd
every night. (That would mess with anyone in the process of paginating.)
How to: MySQL order by user_id (RAND) with pagination
The RAND()
function does not really generate random numbers but what's called pseudo random numbers: numbers are calculated with a deterministic formula and they're just intended to look random. To calculate a new number, you take the previous one and apply the formula to it, and that's how we get different output with a deterministic function: by using different input.
The initial number we use is known as seed. If you have a look at the manual you'll see that RAND()
has an optional argument:
You've probably figured out by now where I want to go:
RAND(), RAND(N)
Returns a random floating-point value v in the range 0 <= v < 1.0. If
a constant integer argument N is specified, it is used as the seed
value, which produces a repeatable sequence of column values
mysql> SELECT language_id, name FROM language ORDER BY RAND(33);
+-------------+----------+
| language_id | name |
+-------------+----------+
| 3 | Japanese |
| 1 | English |
| 4 | Mandarin |
| 6 | German |
| 5 | French |
| 2 | Italian |
+-------------+----------+
6 rows in set (0.00 sec)
mysql> SELECT language_id, name FROM language ORDER BY RAND(33);
+-------------+----------+
| language_id | name |
+-------------+----------+
| 3 | Japanese |
| 1 | English |
| 4 | Mandarin |
| 6 | German |
| 5 | French |
| 2 | Italian |
+-------------+----------+
6 rows in set (0.00 sec)
P.S. The manual is not explicit about the seed range (it just says integer), you might need some extra research (or just some quick testing). Random Result Pagination
Mysql RAND() accepts a seed as an optional argument. Using a seed, it will return the same randomized result set each time.
What you could do is generate a random seed in PHP on the first page request and pass it along to each page using a query string.
Edit: Sorry, I didn't realize the solution was posted already but was deleted.
Random values in MySql query with pagination
You should use a seed for the MySQL RAND to get consistent results. In PHP you do a
$paginationRandSeed = $_GET['paginationRandSeed']?
( (int) $_GET['paginationRandSeed'] ):
rand()
;
and in MySQL you use that seed"SELECT * FROM table ORDER BY RAND(".$paginationRandSeed.") LIMIT 0,20"
Of course you'll need to propagate the initial seed in the page requests.Good luck,
Alin
Related Topics
How to Require PHP Files Relatively (At Different Directory Levels)
Programming Multi-Language PHP Applications
Difference Between Buffered and Unbuffered Queries
How to Route Get and Post for Same Pattern in Laravel
JSON_Encode of Multidimensional Array Giving Different Results
Converting Nsarray -> JSON -> Nsdata -> PHP Server ->JSON Representation
Php-Font-Lib Must Either Be Installed via Composer or Copied to Lib/Php-Font-Lib
How to Install Gmp for PHP7 on Ubuntu
How to Use a Class Method as a Callback Function
Converting Between Timezones in PHP
How to Put the Results of a MySQLi Prepared Statement into an Associative Array
Should a Function Use: Return Null;
Php: Fastest Way to Check for Invalid Characters (All But A-Z, A-Z, 0-9, #, -, ., $)
.Htaccess Rewrite: Subdomain as Get Var and Path as Get Var
(When) Should I Use Type Hinting in PHP
Codeigniter $This->Input->Post() Empty While $_Post Is Working Correctly