Optimizing My Random Row Fetch - php

I have been reading about speed issues in relation to getting a random row via MYSQL and PHP, and wondered how my code might be improved for speed.
I have banner information in my database in a table called "banners" and i want to display a random banner in relation to the position on the page, and add +1 to the view_count for that banner. My method works, but for a busy site where this happens on each page load, can this be improved for speed? Thanks
/* Get banners for position 1 then choose a random one to display */
$banners = mysql_query("SELECT id,title,banner_url,destination FROM ".TBL_BANNERS." WHERE position = '1' AND status = '1'");
$banner_count = mysql_num_rows($banners) - 1;
$rand_offset = mt_rand(0,$banner_count);
$result = mysql_query("SELECT id,title,banner_url,destination FROM ".TBL_BANNERS." LIMIT $rand_offset, 1 ");
$banner_id = mysql_result($result,0,"id");
$banner_title = mysql_result($result,0,"title");
$banner_url = mysql_result($result,0,"banner_url");
$banner_dest = mysql_result($result,0,"destination");
/* Add view to this banner */
$database->addViewToBanner($banner_id);
The last function uses the query:
"UPDATE banners SET view_count = view_count+1 WHERE id = '$banner_id'"
I also need to say, that there probably wont be any more than 100 records in the "banners" table at any one time but there will be holes in the ID. The IDs might go up to say 200 but only half of those will still exist.

Generate a random number in php and drill it that way into the PK
The SQL would be
SELECT id,title,banner_url,destination
FROM TBL_BANNERS
WHERE id = $rand_offset
If you miss, run it again. This gives a very efficient seek to one row which will be better than using limit/offset processing

The ideal way to do it, as you can read in lots of places around the internet, e.g. Anton Titov's blog is to do 2 queries:
SELECT COUNT(*) AS banners FROM quotes
Then generate a random number in your programming language (i.e. PHP use mt_rand). and feed it into this Query:
SELECT * FROM banners LIMIT $generated_number, 1
NOTE: this isn't good to use if your table only has a small number of rows. Generally I still use ORDER BY RAND() up until I know there are going to be more than 100 rows in the table.

Instead of the first SELECT, use this:
$banners_count_result = mysql_query("SELECT COUNT(*) AS num_banners FROM ".TBL_BANNERS." WHERE position = '1' AND status = '1'");
$banner_count = mysql_result($banners_count_result, 0, "num_banners");
Unless you add and remove new banners every couple of seconds, consider caching this result somewhere.

Related

How to limit mysql rows to select newest 50 rows

How to limit mysql rows to select newest 50 rows and have a next button such that next 50 rows are selected without knowing the exact number of rows?
I mean there may be an increment in number of rows in table. Well I will explain it clearly: I was developing a web app as my project on document management system using php mysql html. Everything is done set but while retrieving the documents I mean there may be thousands of documents.
All the documents whatever in my info table are retrieving at a time in home page which was not looking good. So I would like to add pages on such that only newest 50 documents are placed in first page next 50 are in second and so on.
But how come I know the exact number of rows every time and I cannot change the code every time a new document added so... numrows may not be useful I think...
Help me out please...
What you are looking for is called pagination, and the easiest way to implement a simple pagination is using LIMIT x , y in your SQL queries.
You don't really need the total ammount of rows you have, you just need two numbers:
The ammount of elemments you have already queried, so you know where you have to continue the next query.
The ammount of elements you want to list each query (for example 50, as you suggested).
Let's say you want to query the first 50 elements, you should insert at the end of your query LIMIT 0,50, after that you'll need to store somewhere the fact that you have already queried 50 elements, so the next time you change the limit to LIMIT 50,50 (starting from element number 50 and query the 50 following elements).
The order depends on the fields you are making when the entries are inserted. Normally you can update your table and add the field created TIMESTAMP DEFAULT CURRENT_TIMESTAMP and then just use ORDER BY created, because from now on your entries will store the exact time they were created in order to look for the most recent ones (If you have an AUTO_INCREMENT id you can look for the greater values aswell).
This could be an example of this system using php and MySQL:
$page = 1;
if(!empty($_GET['page'])) {
$page = filter_input(INPUT_GET, 'page', FILTER_VALIDATE_INT);
if(false === $page) {
$page = 1;
}
}
// set the number of items to display per page
$items_per_page = 50;
// build query
$offset = ($page - 1) * $items_per_page;
$sql = "SELECT * FROM your_table LIMIT " . $offset . "," . $items_per_page;
I found this post really useful when I first try to make this pagination system, so I recommend you to check it out (is the source of the example aswell).
Hope this helped you and sorry I coudn't provide you a better example since I don't have your code.
Search for pagination using php & mysql. That may become handy with your problem.
To limit a mysql query to fetch 50 rows use LIMIT keyword. You may need to find & store the last row id(50th row) so that you can continue with 51th to 100th rows in the next page.
Post what you have done with your code. Please refer to whathaveyoutried[dot]com
check this example from another post https://stackoverflow.com/a/2616715/6257039, you could make and orber by id, or creation_date desc in your query

What is the difference between while and between, and what is the better choice in my case?

I have a table that have hundreds of rows, and i want to get specific rows, I used the LIMIT and between id and id
In my application i have two text inputs, one is for START NUMBER and one is for END NUMBER, When i use the LIMIT i need to tell the user to make the right calculation to get the right start and end
So for example if i have a table of 3000 rows, and i want to select 100 rows above 2000 the query will be :
Select * from table LIMIT 2000,100
This will select 100 rows above 2000
The between method :
In the between method, i'm running a while function on all the table and i'm using IF statement to get the right id's here is what i'm doing :
Select * from table
$datastart = $_POST["datastart"];
$dataend = $_POST["dataend"];
$firstid = 0;
$lastid = 0;
$varcount6=1;
$sql = "select ID from users_info";
$sqlread = mysqli_query($conn,$sql);
while($row = mysqli_fetch_assoc($sqlread)){
if($datastart==$varcount6){
$firstid = $rowdirstid["ID"];
}
if($varcount6>=$dataend){
$lastid = $rowdirstid["id1"];
break;
}
$varcount6++;
}
So now i have the first id and the last id of the table, next i use another sql query :
Select * from table where id between $firstid and $lastid
Both worked
My question is: what should i use if i'm loading huge amount of data each time ?
Should i go with while ? or the LIMIT will make the job done ?
To begin with, you should never use PHP to get the data required, stick to doing that solely in SQL, as PHP is never needed.
The limit query you're using will not cut it for what you're trying to do, as it will not care what id's the entries has, so, if your id's are not 100% consecutive, you will not get the desired result.
You should use the between query you display at the bottom of your post.
But, since you haven't provided your full code I cannot say wether or not you sanitized that input, but that is always a good thing to keep in mind. It's preferable to use parameterized queries instead aswell.
If your sure the ID are consecutive, use SELECT * FROM t WHERE id BETWEEN a AND b ORDER BY ID ASC
If you use LIMIT, the SQL Engine have to scan and order all the first results.
(and index the id field)

Select a random chunk of 30 numbers from 200 numbers

Currently what I'm trying to do is get a chunk of 30 numbers from a set of 200. For example as this will be used with MySQL, I want to select 30 random images from a database of 200 images. I'd like to be able to have two numbers (a high and low) I could use in the limit statement that would return 30 rows of data like this: SELECT * FROM 'images' LIMIT 20,50 or SELECT * FROM 'images' LIMIT 10,40. I know this probably sounds like a stupid question though my brain is just kinda stumped right now. All help is greatly appreciated! Thanks :)
Simply add ORDER BY RAND() to your query. It is "sufficiently" random.
SELECT FOO FROM BAR ORDER BY RAND() LIMIT 30
Using ORDER BY RAND() is considered an antipattern because you're forcing the database to perform a full table scan and expensive sort operation.
To benefit from query caching, you could make a hybrid solution like:
// take all image ids
$keys = $db->query('SELECT image_id FROM images')->fetchALL(PDO::FETCH_COLUMN, 0);
// pick 30
$random_keys = join(',', array_rand(array_flip($keys), 30));
// query those images only
$values = $db->query("SELECT * FROM images WHERE image_id IN ($random_keys)")->fetchAll(PDO::FETCH_ASSOC);
The above query for the keys can be cached in PHP, so it can be used for more frequent requests. However, when your table becomes in the range of 100k or more, I would suggest creating a separate table with the image ids in a randomized order that you can join against the images table. You can populate this once or a few times per day using the ORDER BY RAND().
I would suggest using PHP's array_rand().
http://php.net/manual/en/function.array-rand.php
Put whatever you want to choose from in an array, and let it pick 20 entries for you. Then, you can use whatever file names you want without having to rely on them being in numerical order.

Mysql query forward and back

I'm wondering what is command for database query that is querying data forward or back between rows? Just like forward and back button. Can someone give me an example? Thanks.
For examp:
$sql_xxxx = mysql_query("SELECT * FROM xxxx WHERE id='$xxx'") or die (mysql_error());
$row_xxxx = mysql_fetch_array($sql_xxxx);
Now I need the two same query but for one forward and in the other backward the data.
Since you tagged PHP and MySQL I think I can guess what you are trying to do;
Usually you can control this with the ID of a record in MySQL, for example:
SELECT * FROM MYTABLE WHERE ID = 1;
In PHP, you can construct dynamic queries to get different results while just changing the value of a variable.
Lets say we now want to take the same query and make it dynamic:
$id = 1;
$query = "SELECT * FROM MYTABLE WHERE ID = $id ";
as long as you execute this query, it will give you the row with ID = 1 , cause now it is taking the value from the $id variable.
If you want the next row, then you execute the same query but now $id must have the next value.
$id = 2;
Having a "NEXT" and "BACK" button can be matter of just adding or substracting 1 to the $id variable.
Still, this is only an example. In most of the cases you should not play with id's like that cause you should not assume that all id's exist, remember that "DELETE" exist;
So you can try to execute better a little query to find the next value like this:
to go foward:
$query= "SELECT MIN(ID) FROM MYTABLE WHERE ID > $id"
// This will get you for sure the closest id next to the current one
and to go back:
$query= "SELECT MAX(ID) FROM MYTABLE WHERE ID < $id"
I hope this helps,
Regards
You can specify a LIMIT in your query, which results in a specific number of rows. For instance, when you end your query with LIMIT 100, 10, you will get 10 results starting at row 100.
This way, you can build prev/next buttons, that when clicked, retrieve the previous or next page of results.
But in most other cases, you will just query everything you need and run through the result set. You do this by querying the result, and fetching each next row of the result.
If you use the LIMIT keyword, the database will happily return a "page" of data, for example;
SELECT * FROM posts ORDER BY post_time LIMIT 40,20; -- will return rows 41-60.
You can make use of that from PHP by just setting the first LIMIT number to page number * page size and the second number to page size (with page number being 0 for the first page). In the example, the page size would be 20 and the page number would be 2 (the third page) and the results will be all the posts from the third page.
There's nothing more to it from the database side, you'll just have to make good use of it from PHP.
if i'm understanding the question correctly, it sounds like you're trying to implement pagination in your queries and you would utilize the 'limit' clause, like so:
SELECT * FROM `your_table` LIMIT 5, 5
This will show records 6, 7, 8, 9, and 10
here's more documentation

Storing randomly picked rows in session?

Storing randomly picked rows in session?
Hi!
I’m building a PHP script that outputs a random word from a MySQL table. Each time the script is refreshed I want a new word to display. (It is connected to jquery – so the data output of the php-file is directly displayed on my page)
However, I want to display each word only once. If all the words are picked, I want the script to reset and start picking over again.
Right now I have done this by setting up an additional table called “visited” and putting all the picked rows from table “wordlist” in there, with the users unique session-id to
prevent results from multiple users to interfere with eachother.
So the query goes like this:
session_start();
$id = session_id();
$random_sql = "SELECT *
FROM wordlist AS a
LEFT JOIN visited AS b ON a.word = b.word
AND b.sessionid = '$id'
WHERE b.word IS NULL
ORDER BY a.weight * rand( ) DESC // Weighted random
LIMIT 1";
$random_row = mysql_query($random_sql);
if(mysql_num_rows($random_row) > 0)
{
while($row = mysql_fetch_row($random_row))
{
$insert_query = "INSERT INTO visited (ID, word, sessionid, date) VALUES ('$row[0]', '$row[1]', '$id', CURDATE())";
$insert = mysql_query($insert_query) or die (mysql_error());
echo $row[1];
}
This works perfectly fine, but I reckon it would be hard for the database to handle many visitors at the same time?
So my question is:
How can I store the information of “visited” words in a session and exclude them from the query?
One more thing: I’m estimating that the wordlist-table will have around 8000 rows. Will this be too many for the ORDER BY RAND-function, and render out to be noticeably slow?
Thanks!
This depends on how much the data must be persistent. If you don't need persistency then session is of course much more efficient in this case. You can store there any PHP data structure, i.e. I guess you'd use associative array in this case.
Regarding performance: if the words are indexed sequentially, you can think of generating the random number as a direct id and just retrieve the particular row directly. ORDER BY RAND() must generate all the numbers and sort them, which is much less efficient than just generate one id like RAND() * MAX(ID).
Read more here.

Categories