PHP MySQL pagination with random ordering

后端 未结 6 1580
生来不讨喜
生来不讨喜 2020-12-05 11:25

This is a problem with a ordering search results on my website,

When a search is made, random results appear on the content page, this page includes pagination too.

相关标签:
6条回答
  • 2020-12-05 11:27

    You have several problems to deal with! I recommend that you go step by step.

    First issue: results they already seen not to appear again

    1. Every item returned, store it in one array. (assuming the index id on the example)
    2. When the user goes to the next page, pass to the query the NOT IN:

    MySQL Query

    SELECT * FROM table WHERE id NOT IN (1, 14, 25, 645) ORDER BY RAND() LIMIT 0,10;
    

    What this does is to match all id that are not 1, 14, 25 or 645.


    As far as the performance issue goes: in a memory efficient way

    SELECT RAND( )
    FROM table
    WHERE id NOT
    IN ( 1, 14, 25, 645 )
    LIMIT 0 , 10
    

    Showing rows 0 - 9 (10 total, Query took 0.0004 sec)

    AND

    SELECT *
    FROM table
    WHERE id NOT
    IN ( 1, 14, 25, 645 )
    ORDER BY RAND( )
    LIMIT 0 , 10
    

    Showing rows 0 - 9 (10 total, Query took 0.0609 sec)

    So, don't use ORDER BY RAND(), preferably use SELECT RAND().

    0 讨论(0)
  • 2020-12-05 11:28

    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;

    UPDATE table SET rand_col = RAND();

    This may not work for your specs, as you seem to require every user to see something different every time they hit page 1.

    0 讨论(0)
  • 2020-12-05 11:31

    Use RAND(SEED). Quoting docs: "If a constant integer argument N is specified, it is used as the seed value." (http://dev.mysql.com/doc/refman/5.0/en/mathematical-functions.html#function_rand).

    In the example above the result order is rand, but it is always the same. You can just change the seed to get a new order.

    SELECT * FROM your_table ORDER BY RAND(351);
    

    You can change the seed every time the user hits the first results page and store it in the user session.

    0 讨论(0)
  • 2020-12-05 11:35

    The combination of

    1. random ordering
    2. pagination
    3. HTTP (stateless)

    is as ugly as it comes: 1. and 2. together need some sort of "persistent randomness", while 3. makes this harder to achieve. On top of this 1. is not a job a RDBMS is optimized to do.

    My suggestion depends on how big your dataset is:

    Few rows (ca. <1K):

    • select all PK values in first query (first page)
    • shuffle these in PHP
    • store shuffled list in session
    • for each page call select the data according to the stored PKs

    Many rows (10K+):

    This assumes, you have an AUTO_INCREMENT unique key called ID with a manageable number of holes. Use a amintenace script if needed (high delete ratio)

    • Use a shuffling function that is parameterized with e.g. the session ID to create a function rand_id(continuous_id)
    • If you need e.g. the records 100,000 to 100,009 calculate $a=array(rand_id(100,000), rand_id(100,001), ... rand_id(100,009));
    • $a=implode(',',$a);
    • $sql="SELECT foo FROM bar WHERE ID IN($a) ORDER BY FIELD(ID,$a)";
    • To take care of the holes in your ID select a few records too many (and throw away the exess), looping on too few records selected.
    0 讨论(0)
  • 2020-12-05 11:38

    First you should stop using the ORDER BY RAND syntax. This will bad for performance in large set of rows.

    You need to manually determine the LIMIT constraints. If you still want to use the random results and you don't want users to see the same results on next page the only way is to save all the result for this search session in database and manipulate this information when user navigate to next page.

    The next thing in web design you should understand - using any random data blocks on your site is very, very, very bad for users visual perception.

    0 讨论(0)
  • 2020-12-05 11:41

    I would have your PHP generate your random record numbers or rows to retrieve, pass those to your query, and save a cookie on the user's client indicating what records they've already seen.

    There's no reason for that user specific data to live on the server (unless you're tracking it, but it's random anyway so who cares).

    0 讨论(0)
提交回复
热议问题