How to select random ID's from database excluding deleted ones? - php

I have a "banners" table which has image names and paths to image files that will be randomly viewed each time visitor navigates to another page.
banners table consists of banner_id (auto_increment, unique, primary, tinyint), banner_name (varchar) and banner_path (varchar) fields.
Banners table will be editable via a control panel. New banners will be ADDED, some banner will be DELETED after a while, and may be UPDATED. General CRUD operations, you know...
Now... Because of my goal is showing banners randomly, I will need a random number generator function which gives ability to exclude specific ones.
To be more clear,
Let's say my table looks like this:
banner_id banner_name banner_path
--------- ------------ ------------
1 First Banner first_banner.jpg
2 Second Banner second_banner.jpg
3 Third Banner third_banner.jpg
I can get random ID by using PHP function like this easily: mt_random(1, 3);
But wait. What if I delete one of these banners?
banner_id banner_name banner_path
--------- ------------ ------------
1 First Banner first_banner.jpg
3 Third Banner third_banner.jpg
In this case, as random output becomes "2", what will happen? There is no a "2" banner_id'd row?? So I must exclude DELETED id's from random generator number range. Will this be the best practise? If so, how can I do that?
I'm completly open for any new ideas which will help me to do thing I want to do.
Please help me to figure out this problem...
Thanks
Thanks.

SELECT FLOOR(RAND() * COUNT(*)) INTO #offset FROM banners;
SELECT * FROM banners LIMIT #offset, 1;

Assuming you can live with the small race window, you could do
SELECT FLOOR(RAND()*COUNT(*)) AS bannercount FROM banners;
and fetch this into $bannercount, next run
SELECT * FROM banners ORDER BY banner_id LIMIT $bannercount,1

Why not just select a random row instead of using PHP to select a random ID to show?
SELECT * FROM `banners` ORDER BY RAND() LIMIT 0,1;
And if you have an array of ID's to exclude
SELECT * FROM `banners`
WHERE `banner_id` NOT IN (/* array values*/)
ORDER BY RAND() LIMIT 0,1;

you will create one array from available ids and generate random numbers from that array

Related

Adding database entries together

I am making a site which allows admins to basically add points for a user.
At this point in time, I have a table, where id_child is unique, and id_points changes. So a constant stream of id_points can come in, however, it will only show the latest id_points, not the total.
I am wondering how I could go about creating a PHP script that could add all of those together.
From the image, the idea is that I want all id_points values added together to give a total, and this is for the same id_child
Use SQL sum() funciton:
select sum(id_points) from table `table_name` where `id_child` = 1
Hope i understood right.
First if you want to show only the latest points added you have to create another table #__points where you will keep every new change of points.
You need 3 columns id as PRIMARY and AUTO_INCRENMENT , pts and user_id . user_id will be FK to id_child.
So when you want to add a new record :
INSERT INTO `#__points` (pts,user_id) VALUES ("$pts",$id)
When you want to select last inserted value for each admin :
SELECT * from `#__points` where user_id=$id ORDER BY id ASC LIMIT 1

update mysql, new data starts at unique id 1

I would like to know how to update my database table with new data but having the latest data be at the top with the unique id starting at 1. Here's what I mean:
First insert query, for example, inserts 3 article topics:
id article
1 firstNews
2 secondNews
3 thirdNews
Then the next time I run a Cron job to check for new articles, for example two new articles appear, I want the two new articles to be in the beginning of the table, like this:
id article
1 NewArticle1
2 NewArticle2
3 firstNews
4 secondNews
5 thirdNews
Hope that made sense. This might be a bad way to do it, I guess I could have a column with insert date() and then get the data out OrderBy date but it has to be an easier way to do this. I think this would be the easiest to output the most recent data from the table...
If I do ORDER BY DESC, it would output NewArticle2 before NewArticle1, which would defeat the purpose...
id article
1 firstNews
2 secondNews
3 thirdNews
4 NewArticle1
5 NewArticle2
So by DESC, id 5 would be the first one output...I was really hoping there was a way around this...
You should never do this. Just insert at the end, and to get the latest articles, use a query:
SELECT * FROM articles ORDER BY id DESC;
In general: Don't fit the data to your query, fit the query to your data. The id is not a position or number, it uniquely identifies that row of data.
You could use a Date columns and sort on that. Or just sort by ID descending.
It's rarely a good idea to change a PK in place. Not least, you mah have child tables using that PK and maybe history tables too
Note that there is no implicit order to a SQL table: you always need an ORDER BY to guarantee the resultset order
You should add another column specifically for sorting, e.g. a date. and add an INDEX on it.
Then the query becomes:
SELECT *
FROM news
ORDER BY newsdate DESC
Latest news comes first
If there are two news items that could be posted at exactly the same time, you may wish to include a secondary sort:
ORDER BY newsdate DESC, id ASC
It shouldn't be your concern at all.
ID is not a number nor a position by any means. It's just an unique identifier.
Means every database row sticks to it's id forever. While you want to assign your "unique" id to the every newcoming news, which makes no sense.
So, just have usual autoincremented id and select it using ORDER BY id DESC to have latest entries first.

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

How to take votes and rank images using PHP and MySQL

I'm trying to build a php ranking system where users can rank an image on a scale of 1-5.
Depending on how an image is ranked decides what its place on the leaderboard (rank number) would be. The rank should change depending on the different ratings it receives from users.
An example of this is the ranking system here. http://www.newgrounds.com/portal/view/601966 (Right hand side, lower down the page.)
I'm just looking for any information which would help me achieve this.
Thanks.
Create a table called votes and tie it to your images table:
VOTES:
vote_id INT(11) PK
user_id INT(11)
image_id INT(11)
score TINYINT(1)
Here are some things you're going to need to know:
You need a database. In your database you need to store each of the images you're ranking, do this in a table called "images". In this table you will give each image an "auto-incrementing" primary key. (this means that for each new row you add to the database the primary key will AUTOMATICALLY be +1 from the row before). This means that each image has a UNIQUE row number next to it - identifying that specific row. Call this column id. (we will reference it in other tables in the column image_id).
Next you need a table called "votes". In this table you can store all sorts of information you might need, but simply all you'll need to store is the unique image number from the "images" table and the value of the vote that someone has cast. You'll end up with something like this:
image_id | vote_value
1 | 3
2 | 5
1 | 3
4 | 1
4 | 3
Now you can query this information to get your leaderboard. The query might look something like this:
SELECT image_id, SUM(vote_value) AS rank FROM votes GROUP BY image_id ORDER_BY rank;
That will give you a list of "image_id"s ordered by their rank (i.e. the total of all the votes).
Then you can go back to your images table and get the information for that image out of that table.
SELECT name, url FROM images WHERE id=#image_id we got above#;
Hope this helps you. :) If you get stuck come back and ask again.

Select Random Row from SQL Using PHP

I want to request 5 random rows from my SQL table using php.
for instance, i need to:
mysql_query("SELECT * FROM catalogue >> not sure what goes here << LIMIT 5");
SELECT * FROM catalogue order by RAND() LIMIT 5
Edit:
For what its worth, Please note that using rand() on a table with large number of rows is going to be slow. This can actually crash your server.
Some Solution:
MediaWiki uses an interesting trick (for Wikipedia's Special:Random feature): the table with the articles has an extra column with a random number (generated when the article is created). To get a random article, generate a random number and get the article with the next larger or smaller (don't recall which) value in the random number column. With an index, this can be very fast. (And MediaWiki is written in PHP and developed for MySQL.)
But this is for a single random row only.
Assuming the table has AUTO INCREMENT on you could get the biggest ID with
SELECT id FROM catalogue ORDER BY id DESC LIMIT 1
and the smallest ID with
SELECT id FROM catalogue ORDER BY id ASC LIMIT 1
making it possible to do this
$randomId = mt_rand($smallestId, $biggestId);
$randomSql = mysql_query("SELECT * FROM catalogue WHERE id='$randomId'");
For five rows you could create the random ID five times.
If you're selecting random rows from a very large table, you may want to experiment with the approaches in the following link:
http://akinas.com/pages/en/blog/mysql_random_row/
note: just to share other options with everyone

Categories