Alright, let's say my MySQL table is set up with entries similar to:
id code sold
1 JKDA983J1KZMN49 0
2 JZMA093KANZB481 1
3 KZLMMA98309Z874 0
I'd like it to pick a random ID within the ranges already in the database (or just go from 1-X) and then just assign it to a variable for my own action to be taken. So let's say we want to pick a code that isn't sold (marked as 0 and not 1), then we'd pick it.
Now, it doesn't have to be 100% random, it could check if the first one is sold, if not, keep going. But I'm not 100% sure on how to go by this. Snippets would be appreciated because I can work out things easily on my own, I just need an example to see where I am going.
How about using a WHERE and ORDER BY RAND()
SELECT id, code
FROM tablename
WHERE sold = 0
ORDER BY RAND()
LIMIT 1
If you don't need the random, then don't use it. It can affect performance very negatively. Since you mentioned in your post that it wasn't necessary, I would recomment using Ezequiel's answer above and dropping the rand. See Most Efficient Way To Retrieve MYSQL data in random order PHP for more info.
It seems that your codes are already random, so why not just take the first item; if you have many unsold records in your database, doing the typical ORDER BY RAND() will hurt the database performance.
SELECT *
FROM codes
WHERE sold = 0
LIMIT 1
FOR UPDATE;
I've also added FOR UPDATE to avoid race conditions, assuming that you're using transactions, as you update the record later (to actually sell it).
Have you tried
SELECT * FROM myTable WHERE sold = 0 ORDER BY RAND() LIMIT 1
Adding ORDER BY RAND() to the rest of your SELECT query is the most straightforward way to accomplish this.
Related
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. I user following as my SQL query.
SELECT * FROM table ORDER BY RAND() LIMIT 0,10;
so my questions are
I need to make sure that everytime user visits the next page, results they already seen not to appear again (exclude them in the next query, in a memory efficient way but still order by rand() )
everytime the visitor goes to the 1st page there is a different sets of results, Is it possible to use pagination with this, or will the ordering always be random.
I can use seed in the MYSQL, however i am not sure how to use that practically ..
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.
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.
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.
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
Every item returned, store it in one array. (assuming the index id on the example)
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().
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).
The combination of
random ordering
pagination
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.
ok; this has been frying my brain for hours. I think I might need a sub query, but I'm not that advanced at this kind of thing so any help or pointers in the right direction would be greatly appreciated.
Here's the Query I have....
$query = "SELECT * FROM events WHERE event_type = 'Christmas Listings' AND event_active='Yes' ORDER BY event_date ASC LIMIT 5";
$result= mysql_query($query);
OK... now for the plain english bit on what I want to achieve (to understand what I'm trying to achieve):
I want to check the event type ('event_type') is what I'm getting (ie. Christmas Listings) as there are multiple types in this column.
I want to check the event is active ('event_active') is Yes(the data in this field is Yes/No).
I want to order them by the ('event_date') ASC (the data in this field is yyyy-mm-dd) so they show the latest entry by its date from the DB.
I want to LIMIT (or in some way control the output) the results to only have 5 results displayed when running this kind of query through a WHILE statement.
OK, this all works BUT; when I get to the actual output display, i'm having a shaky output in how many results are actually display... What happens is if I have multiple events which are switched off, as in event_active is 'Off' then its almost like the argument is counting from the all the results that are (including event_active='Off') and consequently not showing how I expect them to display?
Hope this makes sense.... Any help would be gratefully received.
SELECT *
FROM events
WHERE event_type = 'Christmas Listings' AND event_active='Yes'
ORDER BY event_date
LIMIT 0, 5
so your statement is easyer to read..
You shoul use 1 / 0 instead of Yes / no
The Limit does not count all lines!
First step - doing the query including WHERE
Second step - ORDER BY
Third step - LIMIT
If you have set an index on the colum you sort. The sort will stop after 5 lines,
also means - it get faster
The ASC in the ORDER BY command is not necessary, because ASC is default
I am really not sure what you are asking, but LIMIT works as follows:
The LIMIT means that after your query is done, and ALL WHERE statements are processed, only the first 5 are returned.
ALl results where event_active is not 'Yes' will not be shown, and disregarded in everything.
This result is the same as a result where you would do the query without the limit, and just look at the first 5 lines.
That query should be fine. I'd check your data set (or even better, post it!). You might want to look into normalizing the database too. It'll help you out in the future.
The problem, I think, is with your 'event_active'.
MySQL uses 0 and 1 to indicate whether the field is true/false, yes/no, on/off. Try using 0 and 1 in your SELECT statement unless the field type on that field is VARCHAR and you actually are using those words.
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. I user following as my SQL query.
SELECT * FROM table ORDER BY RAND() LIMIT 0,10;
so my questions are
I need to make sure that everytime user visits the next page, results they already seen not to appear again (exclude them in the next query, in a memory efficient way but still order by rand() )
everytime the visitor goes to the 1st page there is a different sets of results, Is it possible to use pagination with this, or will the ordering always be random.
I can use seed in the MYSQL, however i am not sure how to use that practically ..
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.
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.
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.
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
Every item returned, store it in one array. (assuming the index id on the example)
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().
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).
The combination of
random ordering
pagination
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.
ok; this has been frying my brain for hours. I think I might need a sub query, but I'm not that advanced at this kind of thing so any help or pointers in the right direction would be greatly appreciated.
Here's the Query I have....
$query = "SELECT * FROM events WHERE event_type = 'Christmas Listings' AND event_active='Yes' ORDER BY event_date ASC LIMIT 5";
$result= mysql_query($query);
OK... now for the plain english bit on what I want to achieve (to understand what I'm trying to achieve):
I want to check the event type ('event_type') is what I'm getting (ie. Christmas Listings) as there are multiple types in this column.
I want to check the event is active ('event_active') is Yes(the data in this field is Yes/No).
I want to order them by the ('event_date') ASC (the data in this field is yyyy-mm-dd) so they show the latest entry by its date from the DB.
I want to LIMIT (or in some way control the output) the results to only have 5 results displayed when running this kind of query through a WHILE statement.
OK, this all works BUT; when I get to the actual output display, i'm having a shaky output in how many results are actually display... What happens is if I have multiple events which are switched off, as in event_active is 'Off' then its almost like the argument is counting from the all the results that are (including event_active='Off') and consequently not showing how I expect them to display?
Hope this makes sense.... Any help would be gratefully received.
SELECT *
FROM events
WHERE event_type = 'Christmas Listings' AND event_active='Yes'
ORDER BY event_date
LIMIT 0, 5
so your statement is easyer to read..
You shoul use 1 / 0 instead of Yes / no
The Limit does not count all lines!
First step - doing the query including WHERE
Second step - ORDER BY
Third step - LIMIT
If you have set an index on the colum you sort. The sort will stop after 5 lines,
also means - it get faster
The ASC in the ORDER BY command is not necessary, because ASC is default
I am really not sure what you are asking, but LIMIT works as follows:
The LIMIT means that after your query is done, and ALL WHERE statements are processed, only the first 5 are returned.
ALl results where event_active is not 'Yes' will not be shown, and disregarded in everything.
This result is the same as a result where you would do the query without the limit, and just look at the first 5 lines.
That query should be fine. I'd check your data set (or even better, post it!). You might want to look into normalizing the database too. It'll help you out in the future.
The problem, I think, is with your 'event_active'.
MySQL uses 0 and 1 to indicate whether the field is true/false, yes/no, on/off. Try using 0 and 1 in your SELECT statement unless the field type on that field is VARCHAR and you actually are using those words.
At present im using:
SELECT * from foo ORDER BY RAND() LIMIT 2
to select 2 random items from database but there is the possiblily of the result both being the same item so how can i make it so they are not and both always different?
As always thanks in advance :)
Using ORDER BY RAND() does not result in duplicate rows.
Judging from scattered forum posts and bug reports, this might have been the case in a previous MySQL version (although I strongly doubt this), but it definitely is not anymore. I have just run a couple of tests myself and can confirm that ORDER BY RAND() should never return duplicate rows.