How can I optimize these queries? - php

Consider the following code snippet:
$beat = date('B'); // 1 beat = 86.4 seconds, 1000 beats = 1 day
$total = 'SELECT COUNT(id) FROM ads WHERE featured = 1;'; // number of featured ads
$current = 'SELECT * FROM ads WHERE featured = 1 ORDER BY id ASC LIMIT 1 OFFSET ' . ($beat % $total) . ';'; // current featured ad
Basically this cycles trough all the featured ads giving each one of them a beat (86.4 seconds) window where they will be given special highlight, example:
$beat $total $current
0 3 0
1 3 1
2 3 2
3 3 0
4 3 1
5 3 2
6 3 0
7 3 1
This works just fine, however I was wondering if somehow it would be possible to eliminate the need for the $total query and accomplish the same using just one query.
I don't see anyway to do this without using sub-queries but still, I'd like to hear your opinion on this.

$current = 'SELECT *, FOUND_ROWS(id) as num FROM ads WHERE featured = 1 ORDER BY id ASC LIMIT 1 OFFSET MOD(' . $beat . ', num)';

no, this is not possible. mysql requires an explicit constant value in LIMIT clauses. you can't put a calculation in a LIMIT clause.

You can't optimize queries. But you can optimize algorithm. Probably, you do need to do this, but... ))
If $total >= 1000 you will never show some ads. At any case some ads are shown more times then others.
long timestamp = ... // standard timestamp in millis
long period = 86400; // millis
long total = ... // you total query
long offset = (timestamp / period) % total;
current = ... // you last query, but with my offset

Related

PHP Countdown SQL Rows from Total Rows to 0

I couldn't quite find what I was look for by googling or searching stackoverflow, so I'm just gonna ask it.
$reply_sql = mysqli_query($successconnect, "SELECT * FROM replies WHERE reply_thread = '" . $reply_thread_id . "' ORDER BY reply_date DESC LIMIT $cur, $max");
$numbb = ((20 - (20 * $page)) * -1);
while($reply_row = mysqli_fetch_assoc($reply_sql)) {
$numbb++;
This will result in each reply row counting from 1 up to the total amount of rows. That is all fine if I want my replies to order ASC.
I want it to go from total amount of rows down to 0. I imagine this is all math, but I'm really bad at it. And maybe there's an easier way to do this.
$numbb--;
This I know about, but I tried many variations of $numbb and couldn't quite figure out a way to make for instance:
23 (Total Replies) 1 (Page 1) makes it count from 23 down to 3
23 (Total Replies) 2 (Page 2) makes it count from 3 down to 1
It took me a while of trying different combinations, but I had to move around the original code with 23 or replycount in mind to get the result I need.
$numbb = ((($replycount + 20) - (20 * $page)) + 1);
while($reply_row = mysqli_fetch_assoc($reply_sql)) {
$numbb--;
23 + 20 = 43 - 20 * 1 = 23 + 1 = 24 which $numbb shows as 23 with the first row.
And then 23 + 20 = 43 - 20 * 2 = 3 + 1 = 4 which shows as 3 on page 2.

PHP MySQLi Pagination LIMIT issue

I am currently working on a pagination script for displaying records capped at 20 per page.
I have found a similar question on Stack Overflow, which had an example of how one would perform this. I've tried the code and made adjustments too, but I can't get it to function properly.
The code is:
$pages = ceil($rescnt['cnt']/$limit);
$page = min($pages, filter_input(INPUT_GET, 'page', FILTER_VALIDATE_INT, array(
'options' => array(
'default' => 1,
'min_range' => 1,
),
)));
$offset = ($page - 1) * $limit;
$start = $offset;
$end = min(($offset + $limit), $rescnt['cnt']);
There are 41 records in the MySQL database (0 - 40).
The first page displays correctly, with a LIMIT 0,20. However, the second page's limit is LIMIT 20,40 which actually returns 21 results.
The scripts calculates that there should be 3 pages in total (20 on P1, 20 on P2 & 1 on P3) however, P1 has 20 & P2 has 21. P3 is empty.
I have tried altering the calculation of the pages etc. but it still won't work. Could anyone please give me a little insight into this issue?
Thanks in advance.
FROM 21 to 40, rather than 20.
The LIMIT clauses are inclusive. It is from Record 20, count 20.
LIMIT 20,20 = 20,21,22,...37,38,39.
EDIT:
You will see LIMIT 0,20 shows 20 rows, but they start at zero. Rather like an array key, this perpetuates through the iterations.
BETTER:
You're updating the wrong columns.
First 20 results = LIMIT 0,20
next 20 results = LIMIT 19,20
final 20 results = LIMIT 39,20
LIMIT <start row>, <row count>
20 to 40 is 21 records. You have off by one bug. You should start off the next page with previous page last record plus 1. i.e. 21,40
Like 1 to 2 i.e. 1,2 is two records.

Mysql query to get closest record

I have a table
id startmileage endmileage price
1 1 2 30
2 2 3 50
3 3 4 70
4 4 5 100
5 5 25 4.5
6 25 35 7
In this table i have stored data to get price between mileages (from start to end mile)
I am able to get the price between 1 difference value like 1 to 2, 2 to 3. But for the values between 5 to 25 and 25 to 35 i have tried a query which will work for both the closest and exact value like this
SELECT *
FROM table
ORDER BY ABS( startmileage - myValue )
LIMIT 1
But this query only works for one input (startmileage), As i need to use both start and end mileage to get the best closest record.
Can someone please tell me the best query for this ?
ORDER BY
(
greatest(startmileage - $myvalue ,0)
+
greatest($myvalue - endmileage ,0)
) ASC , startmileage DESC
if the $myvalue is within boundaries, this will be equivalent to zero. If it is before startmileage, first part will rose while second is zero. If $myvalue is after endmileage, first part will be zero and second will rose up.
function greatest( ??? , 0 ) will return ??? only when it is greater then zero, returning zero otherwise.
edit: A bit optimized query (works the same way):
ORDER BY
greatest(startmileage - $myvalue , $myvalue - endmileage ) ASC
, startmileage DESC

split data according to N number of categories with X number of total data limit

OK. I am working in a PHP project.
and I have some categorized data in my database -
For example- I have data of Three Categories.
Category 1
Category 2
Category 3
Categories could be changed. Like 2,3,4,5 etc.
And i have facility to show a limit of data.
Ex. Lets say Admin want to show only 9 Records
So now we have three categories and x limit (which is 9 according to example). and best thing is that-
I need to show N numbers of records from each category and total X records.
Here N numbers is for each Category. So it will look like this:
N1 + N2 + N3 = X (limit ex. 9)
N1 from Category1 N2 from Category2 N3 from Category3
So basically i have formulated it like this -
NumberOfRecordPerCategory = X (limit) / N (Number of Categories)
But it is actually working if i set the limit multiple of 3 like 6,9,12 etc.
So my Question is-
How do i manage the total records according to limit (X) -
1. if Admin set the limit which is not multiple by 3 like 8,10,11, etc.
2. if Admin change the number of category Like 2 or 4, then how could i manage this?
Please give some idea about this. and please let me know if Question is not clear.
What I would choose for your requirement is ceil (round fractions up). If there is some fractions because the number is not multiple by 3, for example 11 with 3 categories, then have the limitation of 4,4,3 for each category. This could be done by having two limit values: limit of total, limit per category.
Here's my suggestion for your needs (code) :
<?php
/*
$data = array (
"category1" => array (1,2,3,4,5),
"category2" => array (10,20,30,40,50),
"category3" => array (100,200,300,400,500),
);
*/
$limit = 11; // given by the admin
$number_of_categories = count($data);
$limit_per_category = ceil ( $limit / $number_of_categories ); // ceil (11 / 3) = 4
$cnt = 0; // tracking the total number of retrieved data.
foreach ($data as $row) {
$cnt_category = 0;
foreach ($row as $item) {
echo $item . "\n"; // print the first N (NumberOfRecordPerCategory) data
$cnt_category++;
$cnt++;
if ($cnt >= $limit) break; // exit the loop
if ($cnt_category >= $limit_per_category) break; // move to next category
}
if ($cnt >= $limit) break; // exit the loop
}

PHP - SQL: fetching results in round robin fashion

I have a table, which consists of 3 fields:
id
name
status
Every time I get the results, it should give me 5 names whose status = 1.
Suppose the db contains following:
id name status
1 A 1
2 B 1
3 C 0
4 D 1
5 E 0
6 F 0
7 H 1
8 I 1
9 J 1
10 K 1
11 L 1
12 M 0
1st time, fetch should return: A,B,D,H,I (5 records)
2nd time, fetch should return: J,K,L,A,B (5 records)
UPDATE: I don't want typical pagenation. Consider I have 12 available names from A1 to A12. The first fetch should return A1-A5, second fetch A6-A10 and third fetch A11, A12, A1, A2, A3. So when I reach the end, I need to get records starting from the first to fill the 5 slots.
i am doing it in php with mysql
This looks like some sort of job allocation script?
You need 2 things:
the highest ID returned last time the script was run (lastID)
a number larger than the maximum ID in the table (bigNum)
Then you can write your query as
SELECT
id, name
FROM
table
WHERE
status=1
ORDER BY
(bignum + id) MOD (bigNum + lastID + 1)
LIMIT 5
Shazaam!
Keep track of the ids of the records returned, and for the following queries do:
select top 5 *
from (
select top 5 *
from MyTable
where status = 1
and id not in (1,2,4,7,8)
order by name
union
select top 5 *
from MyTable
where status = 1
order by name
) a
$q = mysql_query("SELECT name FROM table WHERE status = 1 LIMIT 5);
while ($row = mysql_fetch_row($q))
{
.... //first 5
}
$q = mysql_query("SELECT name FROM table WHERE status = 1 LIMIT 5,5);
while ($row = mysql_fetch_row($q))
{
.... //second 5
}
this uses the offset functionality of mysql- think of it as pagination for your results.

Categories