Mysql query one random? - php

I have issuse with displaying random item from table, is there way to display just one random item, but not the item i preffered to not to be displayed?
I have table PRODUCT
and rows
ID
TITLE
URL_TITLE
and
$link = "coffee";
I want to display random just one product from table PRODUCT, but not the the same i have in $link
What i want to say i just want random item from table, but not when in $link="coffee" to get that random element
p.s
$link in in URL_TITLE row :)

This should help:
SELECT ID, TITLE, URL_TITLE
FROM PRODUCT
WHERE URL_TITLE != "coffee"
ORDER BY RAND()
LIMIT 1;
Note that in some versions of SQL, != is written <>.
Obviously, you want to select all rows or a different subset, just use SELECT * or whatever you need.
Edit: as per HamZa's comment and James' answer, using ORDER BY RAND() is bad practice on large tables. You could potentially generate a random ID and select that, checking that it's not your illegal one, but if you have a whole bunch that you can't select, you could potentially call this query a ton of times (which is bad).

Using rand() in queries is not ideal, especially in large tables, but even small ones as you never know when they'll grow (ie site or service exceeds your expectations etc).
You can use it like this:
select item from product where item != '$link' ORDER BY rand() LIMIT 1
But you should use something better, like temp tables, or you could select all the IDs from the database in one query, use PHP to select a random one from them all, then use the ID selected by PHP and grab the data from the database.

SELECT id
FROM PRODUCT
WHERE URL_TITLE != "coffee"
ORDER BY RAND()
LIMIT 1;

Although I don't know PHP, I think a simple way to do it is to pass the $link value as part of a where condition.
Using 'pure' SQL:
select *
from product
where URL_TITLE <> 'coffee'
order by rand()
limit 1

Related

Pagination: 2 Queries(row-count and data) or 1 larger query

As I do not know anything about speed and complexity of php and mysql(i) scripts, I had this question:
I have a database with 3 tables:
'Products' with about 9 fields. Containing data of products, like 'long' content text.
'Categories' with 2 fields. Containing name of categories
'Productcategories' with 2 fields. Containing which product has which categories. Each product is part of 1-3 categories.
In order to set up pagination (I need row_count because I wish to know what the last page is), I was wondering what the most sufficient way to do it is, and or it depends on the amount of products (50, 100, 500?). The results returned depends on a chosen category:
"SELECT * FROM `productcategories`
JOIN products ON products.proID = productcategories.proID
WHERE productcategories.catID =$category";
Idea 1:
1 query which only selects 1 field, instead of all. And then counts the total rows for my pagination with mysqli_num_rows().
A second query which directly selects 5 or 10 (with LIMIT I expect) products to be actually shown.
Idea 2:
Only 1 query (above), on which you use mysqli_nuw_rows() for row count and later on, filter out the rows you want to show.
I do not know which is the best. Idea 1 seems faster as you have to select a lot less data, but I do not know or the 2 queries needed influence the speed a lot? Which is the fastest: collecting 'big' amounts of data or doing queries?
Feel free to correct me if I am completely on the wrong path with my ideas.
It is generally considered best practice to return as little data as possible so the short answer is to use the two queries.
However, MySQL does provide one interesting function that will allow you to return the row count that would have been returned without the limit clause:
FOUND_ROWS()
Just keep in mind not all dbms' implement this, so use with care.
Example:
mysql> SELECT SQL_CALC_FOUND_ROWS * FROM tbl_name
-> WHERE id > 100 LIMIT 10;
mysql> SELECT FOUND_ROWS();
Use select count(1) as count... for the total number of rows. Then select data as needed for pagination with limit 0,10 or something like that.
Also for total count you don't need to join to the products or categories tables as that would only be used for displaying extra info.
"SELECT count(1) as count FROM `productcategories` WHERE catID=$category";
Then for data:
"SELECT * FROM `productcategories`
JOIN categories ON categories.catID = productcategories.catID
JOIN products ON products.proID = productcategories.proID
WHERE productcategories.catID=$category limit 0,10";
Replacing * with actual fields needed would be better though.

mysql query returning more than one value - should return one

I have a website where visitors can create "battles" and upload videos to compete in these battles.
The following mySQL query is trying to retrieve details of every "battle" in a database.
The problem I have is that if a "battle creator" has uploaded two "videos" to the same battle, a duplicate battle prints out.
How can I make the query only print out one value for each battle, even if the videos table has two entries under the same battle_id?
Thanks!
SELECT * from Battles, Player, Video
WHERE (Battles.battle_creator = Player.player_id
AND Battles.battle_id = Video.battle_id
AND Video.player_id = Battles.battle_creator)
ORDER BY Battles.battle_date DESC;
There's no way to get the information you're asking for from a single query once multiple videos have been assigned to a battle by a single user.
The best way to get all the data for the battle is to separate your query into two subqueries:
SELECT * from Battles, Player
WHERE Battles.battle_creator = Player.player_id
ORDER BY Battles.battle_date DESC;
...and then:
SELECT * from Video
ORDER BY Battles.battle_date DESC, Player.player_id;
The first query will give you one row per battle; the second will give you all videos for all battles, which you can iterate over.
From a scaling perspective, you'll do better to avoid JOINs altogether, so the extra work will be well worth it.
You can either add LIMIT 1 clause to your query to only get first result, or use DISTINCT clause like
SELECT DISTINCT *
FROM ...
That said, you should not use "SELECT * " when querying for more than one table -
use "SELECT table.*" or "SELECT table.field1, table.field2, ..." to be more specific.
You can't do "exactly" that, because your query:
SELECT * from Battles, Player, Video ...
is implicitly asking for all the videos. So you need to ask yourself first, how do I select that one video I want?
If you just want one video, whatever, then add LIMIT 1 to the query and be done with that. ORDER BY video_date ASC or DESC before the LIMIT to retrieve the earliest or latest video.
Otherwise, you have to do something like:
SELECT * from Battles
JOIN Player ON (Battles.battle_creator = Player.player_id)
JOIN Video ON (Battles.battle_id = Video.battle_id
AND Video.player_id = Battles.battle_creator)
WHERE Video.video_id = (SELECT MIN(video_id) FROM Video AS Video2 WHERE
Battles.battle_id = Video2.battle_id
AND Video2.player_id = Battles.battle_creator)
ORDER BY Battles.battle_date DESC;
In the example above I used, as "video choice criterion", "the video with smallest video_id". You will want to have an index on (Video.video_id), something like
CREATE INDEX video_ndx ON Video(player_id, battle_id, video_id);
As Ninsuo's comment points out, the proper way to control this is, after your ORDER BY clause, specify LIMIT 1.
This won't work if you want the entire table, just without duplicates. Consider running some comparison checks on your returned data, or using SELECT DISTINCT.

How to best reorder 100k database rows every hour?

Mysql - i want to reorder a 100k row database every hour. I have a field called 'order' that i sort by. how can i best reorder it?
I currently do this (pseudo):
mainpage.php : select * from table order by `order` desc limit 100;
and hourly:
cronjob.php : select * from table order by rand();
$i=0;
foreach($row) {
$i++;
update table set order = $i where id = $row['id']
}
but it takes ages.
If i just do 'update table set order = rand()' there will be duplicates and i don't want order to have duplicates (but it isn't set to a UNIQUE index because as it is updating there will be duplicates.
whats the best way to go about doing this?
(i do it this way because just doing "select * from table order by rand() limit 100" was really slow, but having it on an index is much faster. it just takes quite a while to reorder it)
(mysql 5)
Why not use an auto increment field and then generate a list of random values (between the min and max stored) to use when selecting?
MySQL isn't a sequential-on-disk storage anyway. This doesn't get you anything. Doing this might make rows show up in your management client in a certain order, but it won't actually add any speed to anything. Please just add an ORDER BY clause to your select statement.
how about creating another table - my_wierd_sort_table.
then delete it entirely, and insert the PK and your rand() column - and use that to order by in your selects...

Getting random results from large tables

I'm trying to get 4 random results from a table that holds approx 7 million records. Additionally, I also want to get 4 random records from the same table that are filtered by category.
Now, as you would imagine doing random sorting on a table this large causes the queries to take a few seconds, which is not ideal.
One other method I thought of for the non-filtered result set would be to just get PHP to select some random numbers between 1 - 7,000,000 or so and then do an IN(...) with the query to only grab those rows - and yes, I know that this method has a caveat in that you may get less than 4 if a record with that id no longer exists.
However, the above method obviously will not work with the category filtering as PHP doesn't know which record numbers belong to which category and hence cannot select the record numbers to select from.
Are there any better ways I can do this? Only way I can think of would be to store the record id's for each category in another table and then select random results from that and then select only those record ID's from the main table in a secondary query; but I'm sure there is a better way!?
You could of course use the RAND() function on a query using a LIMIT and WHERE (for the category). That however as you pointed out, entails a scan of the database which takes time, especially in your case due to the volume of data.
Your other alternative, again as you pointed out, to store id/category_id in another table might prove a bit faster but again there has to be a LIMIT and WHERE on that table which will also contain the same amount of records as the master table.
A different approach (if applicable) would be to have a table per category and store in that the IDs. If your categories are fixed or do not change that often, then you should be able to use that approach. In that case you will effectively remove the WHERE from the clause and getting a RAND() with a LIMIT on each category table would be faster since each category table will contain a subset of records from your main table.
Some other alternatives would be to use a key/value pair database just for that operation. MongoDb or Google AppEngine can help with that and are really fast.
You could also go towards the approach of a Master/Slave in your MySQL. The slave replicates content in real time but when you need to perform the expensive query you query the slave instead of the master, thus passing the load to a different machine.
Finally you could go with Sphinx which is a lot easier to install and maintain. You can then treat each of those category queries as a document search and let Sphinx randomize the results. This way you offset this expensive operation to a different layer and let MySQL continue with other operations.
Just some issues to consider.
Working off your random number approach
Get the max id in the database.
Create a temp table to store your matches.
Loop n times doing the following
Generate a random number between 1 and maxId
Get the first record with a record Id greater than the random number and insert it into your temp table
Your temp table now contains your random results.
Or you could dynamically generate sql with a union to do the query in one step.
SELECT * FROM myTable WHERE ID > RAND() AND Category = zzz LIMIT 1
UNION
SELECT * FROM myTable WHERE ID > RAND() AND Category = zzz LIMIT 1
UNION
SELECT * FROM myTable WHERE ID > RAND() AND Category = zzz LIMIT 1
UNION
SELECT * FROM myTable WHERE ID > RAND() AND Category = zzz LIMIT 1
Note: my sql may not be valid, as I'm not a mySql guy, but the theory should be sound
First you need to get number of rows ... something like this
select count(1) from tbl where category = ?
then select a random number
$offset = rand(1,$rowsNum);
and select a row with offset
select * FROM tbl LIMIT $offset, 1
in this way you avoid missing ids. The only problem is you need to run second query several times. Union may help in this case.
For MySQl you can use
RAND()
SELECT column FROM table
ORDER BY RAND()
LIMIT 4

Randomly Selecting Rows with MySQL

To randomly select records from one table; do I have to always set a temporary variable in PHP? I need some help with selecting random rows within a CodeIgniter model, and then display three different ones in a view every time my homepage is viewed. Does anyone have any thoughts on how to solve this issue? Thanks in advance!
If you don't have a ton of rows, you can simply:
SELECT * FROM myTable ORDER BY RAND() LIMIT 3;
If you have many rows, this will get slow, but for smaller data sets it will work fine.
As Steve Michel mentions in his answer, this method can get very ugly for large tables. His suggestion is a good place to jump off from. If you know the approximate maximum integer PK on the table, you can do something like generating a random number between one and your max PK value, then grab random rows one at a time like:
$q="SELECT * FROM table WHERE id >= {$myRandomValue}";
$row = $db->fetchOne($q); //or whatever CI's interface to grab a single is like
Of course, if you need 3 random rows, you'll have three queries here, but as they're entirely on the PK, they'll be fast(er than randomizing the whole table).
I would do something like:
SELECT * FROM table ORDER BY RAND() LIMIT 1;
This will put the data in a random order and then return only the first row from that random order.
I have this piece of code in production to get a random quote. Using MySQL's RAND function was super slow. Even with 100 quotes in the database, I was noticing a lag time on the website. With this, there was no lag at all.
$result = mysql_query('SELECT COUNT(*) FROM quotes');
$count = mysql_fetch_row($result);
$id = rand(1, $count[0]);
$result = mysql_query("SELECT author, quote FROM quotes WHERE id=$id");
you need a query like this:
SELECT *
FROM tablename
WHERE somefield='something'
ORDER BY RAND() LIMIT 3
It is taken from the second result of
http://www.google.com/search?q=mysql+random
and it should work ;)
Ordering a big table by rand() can be very expensive if the table is very large. MySQL will need to build a temporary table and sort it. If you have primary key and you know how many rows are in the table, use LIMIT x,1 to grab a random row, where x is the number of the row you want to get.

Categories