Sub-queries in a while loop: what is fastest? - php

I have to check a parameter in a while loop by counting the number of lines in a table. Right now I am doing it this way but it seems very inefficient
while ($stmt->fetch()) {
$stmt3 = $mysqli->prepare("SELECT COUNT(DISTINCT id_roster)
FROM dispo
WHERE id_member = ?");
$stmt3->bind_param('i', $id_alerte);
$stmt3->execute();
$stmt3->store_result();
$stmt3->bind_result($number_dispo);
$stmt3->fetch();
echo $number_dispo;
}
Any idea how I can make it faster (without repeating the query in each loop iteration?

SELECT
id_member, COUNT(DISTINCT dispo.id_roster)
FROM
test.dispo
GROUP BY id_member;
For this data:
id_roster;id_member
1;1
1;1
2;1
3;2
4;2
5;2
6;3
produces a list of all id_members together with associated distinct count of id_roster for each one:
id_member;count
1;2
2;3
3;1
You could further limit this query to only certain id_members
by adding a
WHERE id_member IN (1,2)
clause before the GROUP BY.

You need to combine the outer query $stmt with the inner query $stmt3. You've not provided the code for the outer query so i've tried to demonstrate it generally:
SELECT ot.*, rc.id_roster_count
FROM other_table ot
LEFT JOIN
(
SELECT id_member, COUNT(DISTINCT id_roster) AS id_roster_count
FROM dispo GROUP BY id_member
)
AS rc ON ot.id_member = rc.id_member
WHERE ot.other_value > 5
It may be possible to further optimize this. It depends on your other query and table structure.

You can do it by simply extracting all the data in a single query and use group by to format the data and then use the loop to do the desired option.

Related

How to assign two SELECT queries results to single array variable($row)?

There are two queries with same fields names but different tables. I want fetch both select query's results in same array variable $result. I tried few things but nothing working for me. May b my bad day.
two table, need to be fetch in same variable $result.
$sql1="SELECT city,phone,name from table1 where city='NY'";
$result = $conn->query($sql1);
$sql2="SELECT city,phone,name from table2 where city='NY'";
$result = $conn->query($sql2);
I don't want $result lost $sql1 data after assigning same variable ($result) to second query. Please help
You can use UNION and along with that table record identifier to identify table records differently.
$sql1 = "(SELECT city,phone,name, 't1' ttype from table1 where city='NY')
UNION (SELECT city,phone,name,'t2' ttype from table2 where city='NY')";
$result = $conn->query($sql1);
Note: MySQL uses the DISTINCT clause as default when executing UNION queries if nothing is specified.
If you want duplicate records too, then use UNION ALL.
$sql1="SELECT city,phone,name from table1 where city='NY'
UNION
SELECT city,phone,name from table2 where city='NY'";
$result = $conn->query($sql1);

MySQL JOIN pass PHP variable to two tables

I have the following MySQL query in PHP that passes a variable to complete the query:
SELECT * from mobile_tech WHERE uid=$uid order by timestamp DESC limit 0,1
I have the following MySQL JOIN that provides data from two tables:
SELECT mobile_tech.latitude, mobile_tech.longitude, mobile_tech.timestamp, mobile_tech.uid, gbl_qemplisting.EmpNo, gbl_qemplisting.FirstName, gbl_qemplisting.LastName
FROM mobile_tech, gbl_qemplisting
WHERE mobile_tech.uid=gbl_qemplisting.EmpNo AND date(timestamp)='$currentday'
group by uid
I need to combine these two queries into one with a JOIN and still passing the $uid variable to complete the query.
I've tried the following and it did not work:
SELECT mobile_tech.latitude, mobile_tech.longitude, mobile_tech.timestamp, mobile_tech.uid, gbl_qemplisting.EmpNo, gbl_qemplisting.FirstName, gbl_qemplisting.LastName
FROM mobile_tech, gbl_qemplisting
WHERE mobile_tech.uid=$uid AND gbl_qemplisting.EmpNo=$uid AND date(timestamp)='$currentday'
Your query will return a cross product between the mobile_tech and gbl_emplisting rows for $uid. If you just want one row, as in the first query, use ORDER BY and LIMIT similarly.
SELECT mobile_tech.latitude, mobile_tech.longitude, mobile_tech.timestamp, mobile_tech.uid, gbl_qemplisting.EmpNo, gbl_qemplisting.FirstName, gbl_qemplisting.LastName
FROM mobile_tech, gbl_qemplisting
WHERE mobile_tech.uid=$uid AND gbl_qemplisting.EmpNo=$uid AND date(timestamp)='$currentday'
ORDER BY mobile_tech.timestamp
LIMIT 1
Please try with this and say the result
SELECT mobile_tech.latitude,
mobile_tech.longitude,
mobile_tech.timestamp,
mobile_tech.uid,
gbl_qemplisting.EmpNo,
gbl_qemplisting.FirstName,
gbl_qemplisting.LastName
FROM mobile_tech inner join gbl_qemplisting
on mobile_tech.uid=gbl_qemplisting.EmpNo
where mobile_tech.uid=$uid AND date(timestamp)='$currentday'

Mysql query returns error #1093 - You can't specify target table

I need to check and update with same query my database.
The error says it is not possble to update same table which is included in the select statement. Is there any workaround of this to happen in 1 mysql query? Here is the query:
$query='update option_values_to_products set available="0" where id in (
select ovtp.id from option_values ov,option_values_to_products ovtp,options o where
ovtp.product_id="1657" and ovtp.option_values_id=ov.id and ov.options_id=o.id and
o.name="Size" group by ovtp.id )';
Yes, this is a nagging feature of mysql and there is a workaround to it: wrap the subquery within the IN() clause into another subquery.
update option_values_to_products set available="0" where id in (select id from (
select ovtp.id from option_values ov,option_values_to_products ovtp,options o where
ovtp.product_id="1657" and ovtp.option_values_id=ov.id and ov.options_id=o.id and
o.name="Size" group by ovtp.id ) as t)
Avoid using nested queries for many reasons like performance and memory issue, also it can be very hard to be understood for the next developers
Good practice Split your query into 2 parts :
<?php
$qSelect = 'select ovtp.id from option_values ov,option_values_to_products ovtp,options o where
ovtp.product_id="1657" and ovtp.option_values_id=ov.id and ov.options_id=o.id and
o.name="Size" group by ovtp.id';
$res = DATABASE_MANAGER::exec($qSelect);
$qUpdate = 'update option_values_to_products set available="0" where id in (' . implode(",", $res) .')';
$res2 = DATABASE_MANAGER::exec($qUpdate);

PHP + Mysql Select and Count

I need get specific values and count all values from a MySQL table, i need get the best performance, my question is: What is better and faster?
- Use two separate queries:
$TBCount = $Resps = $MySQL->query('SELECT COUNT(*) FROM T1');
$Resps = $MySQL->query('SELECT id, name FROM T1 LIMIT 1');
while ($Resp = $Resps->fetch_assoc()) {
...
}
- Use One query with two SELECT:
$Resps = $MySQL->query('SELECT id, name, (SELECT COUNT(*) FROM T1) AS count FROM T1 LIMIT 1');
while ($Resp = $Resps->fetch_assoc()) {
$TBCount = $Resp['count'];
...
}
- Or someone have some best idea?
In the case of "One query, two SELECT", in the "while" loop, how can i get the count value outside of the loop? (to avoid unnecessary rewrite of the variable).
I would suggest first option with minor modification.
$TBCount = $Resps = $MySQL->query('SELECT COUNT(id) FROM T1');
$Resps = $MySQL->query('SELECT id, name FROM T1 LIMIT 1');
while ($Resp = $Resps->fetch_assoc()) {
...
}
note that I have mentioned just id in count query, this can be a good performance impact while dealing with large data.
You can do the same in second option, but as per my thought process second option will give count in all row returned with the main query. And that value will be same for all rows which is really not required.
That is the reason I would go with first option for separate queries.
$Resps = $MySQL->query('SELECT id, name, (SELECT COUNT(*) as tot FROM T1) AS count FROM T1 LIMIT 1');
$Resp = $Resps->fetch_assoc()
$TBCount = $Resp['tot'];
mysqli_data_seek($Resps, 0);
for finding the count value you do not need to repeatedly loop again and again for the rest of the values use the while loop.
while ($Resp = $Resps->fetch_assoc()) {
//rest of your code
...
}
Take a look at SQL_CALC_FOUND_ROWS. It does everything you want. It will give you the total results, even though you're limiting the data returned.
> SELECT SQL_CALC_FOUND_ROWS id, name FROM T1 LIMIT 1
> SELECT FOUND_ROWS();

Limit SQL join when using CodeIgniter active record class

I'm getting a product listing. Each product may have 1 or more image, I only want to return the first image.
$this->db->select('p.product_id, p.product_name i.img_name, i.img_ext');
$this->db->join('products_images i', 'i.product_id = p.product_id', 'left');
$query = $this->db->get('products p');
Is there anyway to limit the db->join to 1 record using the CI active record class?
Add $this->db->limit(1); before calling $this->db->get('products p');. See the docs at ellislab.com: search the page for limit.
EDIT: I misread the fact that you were trying to apply the LIMIT to the internal JOIN statement.
No. Since you can not do a LIMIT on an internal JOIN statement in regular SQL you can not do it with Code Igniter's ActiveRecord class.
You can achieve what you want using $this->db->group_by with a left join:
$this->db->select('products.id, products.product_name, products_images.img_name, products_images.img_ext');
$this->db->from('products');
$this->db->join('products_images', 'products_images.product_id = products.id', 'left');
$this->db->group_by('products.id');
$query = $this->db->get();
This should give you results by products.id (without repetition of products), with the first matching record from products_images joined to each result row. If there's no matching row from the joined table (i.e. if an image is missing) you'll get null values for the products_images fields but will still see a result from the products table.
To expand on #Femi's answer:
There's no good way to limit the JOIN, and, in fact, you don't really want to. Assuming both products_image.product_id and products.id have indexes (and they absolutely should if you're going to join against them repeatedly) when the database engine does a join, it uses the indexes to determine what rows it needs to fetch. Then the engine uses the results to determine where on the disk to find the records it needs. If you
You should be able to see the difference by running these SQL statements:
EXPLAIN
SELECT p.product_id, p.product_name, i.img_name, i.img_ext
FROM products p
LEFT JOIN products_images i
ON i.product_id = p.product_id
as opposed to:
EXPLAIN
SELECT p.product_id, p.product_name, i.img_name, i.img_ext
FROM (SELECT product_id, product_name FROM products) p
LEFT JOIN (SELECT img_name, img_ext FROM products_images) i
ON i.product_id = p.product_id
The first query should have an index, the second one will not. There should be a performance difference if there's a significant number of rows the the DB.
Had this issue too the way I solved it was iterating over the results and removing the current object if the product_id had existed in a previous one. Create a array, push the product_id's to it while checking if they are repeats.
$product_array = array();
$i = 0;
foreach($result as $r){
if(in_array($r->product_id,$product_array)){
unset($result[$i]);
}else{
array_push($product_array,$r->product_id);
}
$i++;
}
$result = array_values($result); //re-index result array
Now $result is what we want

Categories