Equivalent of SQL_CALC_FOUND_ROWS NULL as rows, for SUM? - php

I am currently using SQL_CALC_FOUND_ROWS NULL as rows, as part of a complex join statement to get the total number of rows in a query based on search values that also contains a LIMIT.
Then I return the following.
$response['total'] = $this->db->query('SELECT FOUND_ROWS() count;')->row()->count;
$response['limit'] = $limit;
$response['start'] = $start;
$response['from'] = $start + 1;
$response['to'] = $start + $query->num_rows();
$response['count'] = $query->num_rows();
$response['records'] = $query->result();
Here Limit might be 20, start might be 0, total is the full set of records for example 87 and records in the LIMIT set of records, eg 20.
Is there a way to also SUM one of my other fields which holds dollar amounts, to get a total dollar amount for the entire set of (87) records, rather than the sub-set after the LIMIT gets applied?

Related

Select query with LIMIT and UNION - Performance Problem

I would like to SELECT certain data out of my mysql DB. I am working with a php loop and a sql statement with a LIMIT and UNION.
Problem: The speed of my query is terrible. One UNION statement tooks 2-4 seconds. Due to the loop the Overall-Query takes 3 Minutes.
Is there a chance to optimize my query?
I tried to separate the "three" statements and merge the results. But this is not really faster. So I think that the UNION is not my problem.
PHP/SQL:
My code is running through two-foreach-loops. The code is working properly. But the performance is the problem.
$sql_country = "SELECT country FROM country_list";
foreach ($db->query($sql_country) as $row_country) { //first loop (150 entries)
$sql_color = "SELECT color FROM color_list";
foreach ($db->query($sql_color) as $row_color) { //second loop (10 entries)
$sql_all = "(SELECT ID, price FROM company
WHERE country = '".$row_country['country']."'
AND color = '".$row_color['color']."'
AND price BETWEEN 2.5 AND 4.5
order by price DESC LIMIT 2)
UNION
(SELECT ID, price FROM company
WHERE country = '".$row_country['country']."'
AND color = '".$row_color['color']."'
AND price BETWEEN 5.5 AND 8.2
order by price DESC LIMIT 2)
UNION
(SELECT ID, price FROM company
WHERE country = '".$row_country['country']."'
AND color = '".$row_color['color']."'
AND price BETWEEN 8.5 AND 10.8
order by price DESC LIMIT 2)";
foreach ($db->query($sql_all) as $row_all) {
$shopID[] = $row_all['ID']; //I just need these IDs
}
}
}
Do you have any idea or hints to get this faster?
An index on (country, color, price, ID) should improve the performance of single queries from seconds to a couple of milliseconds or even less. But you still have the problem of executing 1500 queries. Depending on your system, a single query execution can add an overhead of about 10 ms, which would add up to 15 seconds in your case. You need to find a way to minimize the number of queries - In best case to a single query.
For low limits (like 2 in your case), you can combine multiple LIMIT 1 subqueries with different offsets. I would generate such a query dynamically.
$priceRanges = [
['2.5', '4.5'],
['5.5', '8.2'],
['8.5', '10.8'],
];
$limit = 2;
$offsets = range(0, $limit - 1);
$queryParts = [];
foreach ($priceRanges as $range) {
$rangeFrom = $range[0];
$rangeTo = $range[1];
foreach ($offsets as $offset) {
$queryParts[] = "
select (
select ID
from company cmp
where cmp.country = cnt.country
and cmp.color = clr.color
and cmp.price between {$rangeFrom} AND {$rangeTo}
order by cmp.price desc
limit 1
offset {$offset}
) as ID
from country_list cnt
cross join color_list clr
having ID is not null
";
}
}
$query = implode(' UNION ALL ', $queryParts);
This will generate a quite long UNION query. You can see a PHP demo on rexester.com and SQL demo on db-fiddle.com.
I can't guarantee it will be any faster. But it's worth a try.

Finding a row with greater or equal value to the given number (mysql)

I'm creating a rank system and ranks are stored in a database, my problem is about finding rows with greater or equal value to given user xp:
It's something like this:
list of ranks in ranks table:
name: Rank1 exp: 400
name: Rank2 exp: 500
name: Rank3 exp: 700
$xp = "500";
$find = $db->query("SELECT * FROM ranks WHERE exp >= '$xp' LIMIT 1");
if($find->num_rows > 0){
// if a rank with greater than or equal value was found
$rdata = $find->fetch_assoc();
// return rank name
echo $rdata["name"];
}
this returns Rank1, i don't know why :(
Can anyone help me? I'm not yet good in mysql.
Remove the " " around 500, but for future reference I'd turn this into a prepared statement as your query isn't as secure as it could be
$xp = 500;
$query = "SELECT name FROM ranks WHERE exp >= ?' LIMIT 1";
$stmt = $db->prepare($query);
$stmt->bind_param("s",$xp);
$stmt->execute();
$stmt->bind_param($name);
while($stmt->fetch()){
if($stmt->num_rows > 0 ){
echo $name;
}
}
Another suggestion. Instead of selecting where >= $xp and limiting by 1, Why not select the range you're actually looking for? between 500 and 700 so it doesn't bring back 700?
You did put single quotes around $xp in the query, bit it is probably a numeric value, not a string. Now you are probably comparing a numeric value with a string.
Try:
$find = $db->query("SELECT * FROM ranks WHERE exp >= $xp LIMIT 1");

how to get a specific id within 5 rows in a paging query in Mysql

I have a function in php I use it for paging it is like this :
$query = "SELECT id,
FROM table
ORDER BY id ASC
LIMIT $offset,5";
this work fine but what I want is to get the page that contain id number let say 10 and with it the other 4 rows, I want it to return something like this:
7,8,9,10,11,12 -> if I give it id number 10.
25,26,27,28,29 -> if I give it id number 26 and so on.
like it would return the 5 rows but I want to know how to set the offset that will get me
the page that have the 5 rows with the specified id included.
what should I do like adding where clause or something to get what I want!
Notice that the IDs in your table won't be consecutive if you delete some rows. The code below should work in such conditions:
$result = mysql_query('select count(*) from table where id < ' . $someId);
$offset = mysql_result($result, 0, 0);
$result = mysql_query('select * from table order by id limit ' . max($offset - 2, 0) . ',5');
while ($row = mysql_fetch_assoc($result)) {
print_r($row);
}
Try something like this
//but for pagination to work $page should be $page*$limit, so new rows will come to your page
$limit = 5;
$start = ($page*limit) -2; // for normal pagination
$start = $page -2; // for your case, if you want ids around the $page value - in this case for id = 10 you will get 8 9 10 11 12
if ($start < 0) $start = 0; // for first page not to try and get negative values
$query = "SELECT id,
FROM rowa
ORDER BY id ASC
LIMIT $start,$limit";

PHP: Mysql limit range numbers

I would like get number of records in a table then divide them by 4, after dividing them by 4 i want to create sql statements with limit ranges based on my result. For example I have a table with 8 records I divide by 4, I will create 2 sql statements with a limit range like limit 0,4 and limit 4,8
Final results will look like
Select * from prop where id=123 LIMIT 0,4
Select * from prop where id=123 LIMIT 4,8
My approach was to have for loop which will count the number of sql statements to be made.
Then in the loop: first circle 0-4 and second will be 4-8
Am struggling on the limit 0-4 and limit 4-8
PHP script
include('connect.php');
$query_1 = "Select COUNT(*) as Total from prop where ref = 'SB2004'";
$results_query_1 = mysql_query($query_1);
while($row_query_1 = mysql_fetch_array($results_query_1))
{
$cnt = $row_query_1['Total'];
}
echo $cnt;
echo "<br>";
$num_grps = 0;
if ($cnt % 4 == 0 )
{
echo $num_grps = $cnt / 4 ;
}
$count_chk= $num_grps * 4;
for ($i=1;$i<=$num_grps;$i++)
{
//for loop for range
for()
{
$range = '0,4';
echo "SELECT prop_ref from prop limit".$range;
}
}
Either you've not understood the problem or haven't explained it very well.
The most immediate problem here is that you have misunderstood the syntax for the LIMIT clause. The first argument specifies the offset to start at and the second defines the number of rows to return, hence LIMIT 4,8 will return 8 rows (assuming there are 12 or more rows in the dataset).
The next issue is that you've not said if the results need to be reproducible - e.g. if you have rows with primary keys 1 and 2, should these always be returned in the same query. In the absence of an explicit ORDER BY clause, the rows will be returned based on the order in which they are found by the query.
The next issue is that you've not explained how you want to deal with the last case when the total number of rows is not an even multiple of 4.
The code you've provided counts the number of rows where ref = 'SB2004' but then creates queries which are not filtered - why?
The code you've provided does not change the limit in the queries - why?
The next issue is that there is never a good reason for running SELECT queries inside a loop like this. You didn't exlpain what you intend doing with the select queries. But based on the subsequent update....
include('connect.php');
$query_1 = "Select COUNT(*) as Total from prop where ref = 'SB2004'";
$cnt = mysql_fetch_assoc(mysql_query($query_1));
$blocks=$cnt['Total']/4 + (0 == $cnt['Total'] % 4 ? 0 : 1);
$qry2="SELECT * FROM prop where ref='SB2004' ORDER BY primary_key";
$res=mysql_fetch_assoc($qry2);
for ($x=0; $x<$blocks; $x++) {
print "<div>\n$block<br />\n";
for ($y=0; $y<4; $y++) {
print implode(",", #mysql_fetch_assoc($res)). "\n";
}
print "</div>\n";
}
It's trivial to refine this further to only issue a single query to the database.
If you really must generate individual SELECTs....
include('connect.php');
$query_1 = "Select COUNT(*) as Total from prop where ref = 'SB2004'";
$cnt = mysql_fetch_assoc(mysql_query($query_1));
$blocks=$cnt['Total']/4 + (0 == $cnt['Total'] % 4 ? 0 : 1);
for ($x=0; $x<$blocks; $x++) {
$y=$x*4;
print "SELECT * FROM prop where ref='SB2004'
ORDER BY primary_key LIMIT $y,4<br />\n"
}

code igniter SQL_CALC_FOUND_ROWS

How to get total number of rows for particular query and also limiting the query results to 10 rows. For example. I've a table called sample. It has 400 rows. On running a query like where name = "%Sam%" it returns me 213 rows. Now I'l be taking only first 10 rows and displaying the result to user but I need the total rows returned. How should I need to do it in code igniter?
SELECT
SQL_CALC_FOUND_ROWS *
FROM
sample
WHERE
name like "%sam%"
like this?
How to retrieve total number counts?
You need to run a second query to get the results of SQL_CALC_FOUND_ROWS:
SELECT FOUND_ROWS()
That will return the value found using SQL_CALC_FOUND_ROWS. You may find using an alias easier for getting the result, though:
SELECT FOUND_ROWS() AS num_results
You can use two query one to get the count and the other return the limited result.
Like in your model create a function that only returns the count variable
and create a second function that generates the paginated results.
eg..
public function count($where){
$query = $this->db->query("select count(id) as count from clients $where");
return $query->row('count');
}
}
public function limit($sidx,$sord,$start,$limit,$where){
$query = $this->db->query("select * from clients $where ORDER BY $sidx $sord LIMIT $start , $limit");
if ($query->num_rows() > 0){
return $query->result_array();
}
}
And here goes the controller code
$where = // calculated
$count = count($this->model->count($where));
if( $count > 0 ) { $total_pages = ceil($count/$limit); } else { $total_pages = 0; }
if ($page > $total_pages) $page = $total_pages;
$start = $limit * $page - $limit;
$users = $this->model->limit($sidx,$sord,$start,$limit,$where);
.................

Categories