I have a nested loop in my PHP code that is taking a long time and wondered of there was a way to speed this up:
$sql = "SELECT * FROM table_1";
$result = mysqli_query($sql);
while($row = mysqli_fetch_array($result)){
$sql2 = "
SELECT count(*)
FROM user_modules
WHERE
begin_ymdhi >= ".$date_from." AND
begin_ymdhi <= ".$date_to." AND
(
completed_ymdhi IS NULL OR
completed_ymdhi = ''
) AND
user_id = ".$row['user_id']." AND
task_id = ".$row['task_id']." AND
module_discon = 'N'
";
}
The outer query gets 1000 rows, the inner has to count across 10,000 rows - the run-time is around 115 seconds ! Is there any way to improve this method, either using a different technique or combined SQL query?
Don't use nested queries, combine them into a single query with a join:
SELECT t1.*, COUNT(u.user_id) ct
FROM table_1 t1
LEFT JOIN user_modules AS u ON u.user_id = t1.user_id AND u.task_id = t1.task_id
AND u.begin_ymdhi BETWEEN '$date_from' AND '$date_to'
AND u.module_discon = 'N'
GROUP BY t1.user_id, t1.task_id
Are the task_id's unique? if so, the most straight forward would be something like:
$sql2 = "
SELECT count(task_id) AS TaskCount
FROM user_modules
WHERE
begin_ymdhi >= ".$date_from." AND
begin_ymdhi <= ".$date_to." AND
(
completed_ymdhi IS NULL OR
completed_ymdhi = ''
) AND module_discon = 'N'
group by user_id
";
$result = mysqli_query($sql2);
SELECT user_modules.user_id, user_modules.task_id, count(*)
FROM user_modules LEFT JOIN table_1 USING (user_id, task_id)
WHERE
begin_ymdhi >= ".$date_from." AND
begin_ymdhi <= ".$date_to." AND
module_discon = 'N' AND
(
completed_ymdhi IS NULL OR
completed_ymdhi = ''
)
GROUP BY user_modules.user_id, user_modules.task_id
Append EXPLAIN before that entire SELECT statement (i.e EXPLAIN SELECT count(*)...) and MySQL will give you a run-down on what the select is doing.
Make sure the begin_ymdhi field is indexed properly. SHOW INDEX FROM table_2 to see.
Related
I need to run a query like below
SELECT `loginname`, `id`, `locality`, `wherefrom`
FROM `Volley` JOIN `register` ON loginname=username
WHERE CONCAT_WS('', wherefrom,locality) LIKE '%$search%'
from this result i need to take id and pass it to the other query to check that id exist in another table like below.
SELECT IF(EXISTS(SELECT *
FROM `likes`
WHERE Volleyid = '20'),1,0)
Can i combine this into a single query? For the first one i may get more that 1 rows. I tried and got the results of first query into a array but i am not able to get that particular value from array.
$data = array();
for ($x = 0; $x < mysqli_num_rows($query1); $x++)
{
$data[] = mysqli_fetch_assoc($query1);
}
SELECT IF(EXISTS(SELECT * From
(
SELECT `loginname`,
`id`,
`locality`,
`wherefrom`,
Volleyid
FROM `Volley`
JOIN `register`
ON loginname=username
WHERE CONCAT_WS('', wherefrom,locality) LIKE '%$search%'
) AS T1
WHERE Volleyid = '20'),1,0)
You can use a subquery with IN clause (i think what you what it ś that Volleyid it's equal to id):
SELECT IF(EXISTS(SELECT *
FROM `likes`
WHERE Volleyid IN
(SELECT `id`
FROM `Volley` JOIN `register` ON loginname=username
WHERE CONCAT_WS('', wherefrom,locality) LIKE '%$search%')),1,0)
If you want to get loginname, id, locality, wherefrom columns in the first query alongside with the 1 or 0 flag checked against table likes in single result set. You can write query that looks like this:
SELECT v.*, IF(l.Volleyid IS NOT NULL, 1, 0) as existFlag
FROM
(SELECT `loginname`, `id`, `locality`, `wherefrom`
FROM `Volley`
JOIN `register` ON loginname=username
WHERE CONCAT_WS('', wherefrom,locality) LIKE '%$search%') AS v
LEFT JOIN
(SELECT DISTINCT Volleyid
FROM `likes`) l ON v.id = l.Volleyid;
What I'm trying to do here, is I want to output the count of the id and user_id columns from items, by users which have the rank 1.
Let's say there's 8 rows in items which have a base_item of 400, and these 8 rows, where 2 rows of them has a user_id of 4 and the existing left has a user_id of 6 The output should then be:
$countItems = 8 and $countUsers = 2.
$stmt = $conn->pdo->prepare("
SELECT COUNT(*),`user_id` FROM `items`
INNER JOIN `users`
ON `users`.`id` = `items`.`user_id`
WHERE `base_item` = :i AND `rank` = 1
GROUP BY `user_id`,`items`.`id`
");
$data = array(':i' => '400'); // item_id in `values` table
$stmt->execute($data);
if($inv = $stmt->fetch($db->FETCH_ASSOC)) {
$countItems = $inv['id'];
$countUsers = $inv['user_id'];
}
I have tried several methods, but I still keep getting output 2 and 2 even though the results should be 8 and 2.
Table Info for values:
Table Info for items:
First of all you never want to fetchAll() and discard the data just to count the number of rows. There is a better way.
SELECT COUNT(*) FROM `items` WHERE `base_item` = :item AND `user_id` = :uid
Secondly, you should try to avoid a nested loop whenever possible because nested loops don't scale very well. And the solution is to use a join or a subquery. You haven't posted your table structures, so this query is untested.
SELECT COUNT(*), user_id FROM `items` INNER JOIN users
ON users.id = items.user_id
WHERE `base_item` = :item and rank =1 GROUP BY user_id
One query instead of two, a lot less number of lines of code.
Problem is that you're replacing $smst withing the foreach loop.
Use other variable name, for example:
$stmt->execute();
foreach($stmt as $user) {
$stmt2 = $conn->pdo->prepare("SELECT `id`,`user_id` FROM `items` WHERE `base_item` = :item AND `user_id` = :uid");
$stmt2->bindParam(':item', $cmsval['item_id'], $db->PARAM_INT);
$stmt2->bindParam(':uid', $user['id'], $db->PARAM_INT);
Well, all I actually had to do, was remove GROUP BY and add COUNT DISTINCT instead, like following:
$stmt = $conn->pdo->prepare("
SELECT COUNT(DISTINCT `user_id`) as `uid`,
COUNT(`items`.`id`) as `id`
FROM `items`
INNER JOIN `users`
ON `users`.`id` = `items`.`user_id`
WHERE `base_item` = :i AND `rank` = 1
");
$stmt->bindParam(':i', $cmsval['item_id'], $db->PARAM_INT);
$stmt->execute();
if($inv = $stmt->fetch($db->FETCH_ASSOC)) {
$countItems = $inv['id'];
$countUsers = $inv['uid'];
}
I have this query for select a list
$sql="select * from buy_log where (client_id not in
(select selected_client_id from action_log where media_id=buy_log.media_id and client_id=$client_id )
and totalVisit>0 and coin_id=3 or coin_id=4 or coin_id=5)";
and this is my second select
while($row=mysqli_fetch_assoc($result)){
$media_id = $row['media_id'];
$sql2 = "select * from comment_media,comment_list where comment_list.id=comment_media.id and media_id='$media_id' order by rand() limit 2";
}
i try to use join for one query and this is my try :
SELECT S.*,WW.Comments,WW.Comments_id
FROM buy_log S
INNER JOIN
(
SELECT M.media_id,GROUP_CONCAT(W.Comment SEPARATOR '!###!') as Comments,GROUP_CONCAT(W.id SEPARATOR ',') as Comments_id
FROM comment_list W,comment_media as M
Where W.id=M.id and M.media_id='$media_id' and W.used = 0
) WW
ON S.media_id = WW.media_id
where (client_id not in
(select selected_client_id from action_log where media_id=S.media_id and client_id='1234' )
and totalVisit>0 and coin_id=3 or coin_id=4 or coin_id=5)
but here is my problem
Where W.id=M.id and M.media_id='$media_id' and W.used = 0
I don't know how can i set $media_id
any solution ?
UPDATE
Tables Info :
Tables Info : Pic
This should work:
You're extracting media id from that original query, looping through results and using the media id to assign the value $media_id in the php loop, where you perform another query.
The join seems syntactically right just need to reference the buy_log table inside the subquery.
SELECT S.*,WW.Comments,WW.Comments_id
FROM buy_log S
INNER JOIN
(
SELECT M.media_id,GROUP_CONCAT(W.Comment SEPARATOR '!###!') as Comments,GROUP_CONCAT(W.id SEPARATOR ',') as Comments_id
FROM comment_list W,comment_media as M
Where W.id=M.id and M.media_id= S.media_id and W.used = 0
) WW
ON S.media_id = WW.media_id
where (client_id not in
(select selected_client_id from action_log where media_id=S.media_id and client_id='1234' )
and totalVisit>0 and coin_id=3 or coin_id=4 or coin_id=5)
I guess that first query can be rewritten as follows... does it help?
SELECT b.media_id
FROM buy_log b
LEFT
JOIN action_log a
ON a.selected_client_id = b.client_id
AND a.media_id = b.media_id
AND a.client_id = $client_id
WHERE b.totalVisit > 0
AND b.coin_id IN(3,4,5)
AND a.selected_client_id IS NULL;
Hi all I need a solution for this question here is my query look like it works.But takes too much time because of sub queries.Give an alternate query to this query
SELECT *
FROM `room_types`
WHERE id
IN (SELECT capacity
FROM rooms
WHERE id
IN (
SELECT DISTINCT room_id
FROM `reservations`
WHERE DATE(
START ) >= '2016-01-10'
AND DATE(
END ) <= '2016-01-15'
AND STATUS = 'CheckedOut'
AND id
IN (
SELECT op_no
FROM `bills`
WHERE billed = 'Yes'
)
)
)
As of your provided query, Try this:
SELECT
`room_types`.*
FROM
`room_types`
INNER JOIN `rooms` ON (`room_types`.`id` = `rooms`.`id`)
INNER JOIN `reservations` ON (`rooms`.`id` = `reservations`.`room_id`)
INNER JOIN `bills` ON (`reservations`.`id` = `bills`.`op_no`)
WHERE
DATE(`reservations`.`START`) BETWEEN '2016-01-10' AND '2016-01-15'
AND `reservations`.`STATUS` = 'CheckedOut'
AND `bills`.`billed` = 'Yes'
Also you can index columns which will make it more faster.
So.. First try without Database Schema !
SELECT *
FROM room_types rt
JOIN rooms r ON r.capacity = rt.id
JOIN reservations resa ON r.id = resa.room_id
AND DATE(resa.start ) >= '2016-01-10'
AND DATE(resa.end ) <= '2016-01-15'
AND resa.status LIKE "CheckedOut"
JOIN bills b ON resa.id = b.resa_id AND b.billed LIKE "Yes"
Notice that "rt", "r", "resa" and "b" are aliases for your tables
I have five different SQL queries that I would like to return back the number of rows returned in a list. Basically I have a MySQL database and would like to show some statistics on how many records we have and how many of them have had information added. Examples of the queries are...
SELECT * FROM `ibf_ccs_custom_database_1`
SELECT * FROM `ibf_ccs_custom_database_2`
SELECT * FROM `ibf_ccs_custom_database_3`
SELECT * FROM `ibf_ccs_custom_database_1` WHERE `field_30` <> ''
SELECT * FROM `ibf_ccs_custom_database_2` WHERE `field_60` <> ''
Which should return back the following output
Total Records Database 1: 12,548
Total Records Database 2: 9,835
Total Records Database 3: 5,916
Filled Out Records in Database 1: 567
Filled Out Records in Database 2: 681
Any help is much appreciated!
It would be much more efficient to have the database prepare a result containing just the count of rows, rather than preparing a complete set of ten thousand plus rows.
For example, to have the database return the results as shown in the question:
SELECT CONCAT('Total Records Database 1: ',FORMAT(COUNT(1),0) AS txt
FROM `ibf_ccs_custom_database_1`
UNION ALL
SELECT CONCAT('Total Records Database 2: ',FORMAT(COUNT(1),0)
FROM `ibf_ccs_custom_database_2`
UNION ALL
SELECT CONCAT('Total Records Database 3: ',FORMAT(COUNT(1),0)
FROM `ibf_ccs_custom_database_3`
UNION ALL
SELECT CONCAT('Filled Out Records in Database 1: ',FORMAT(COUNT(1),0)
FROM `ibf_ccs_custom_database_1` WHERE `field_30` <> ''
UNION ALL
SELECT CONCAT('Filled Out Records in Database 1: ',FORMAT(COUNT(1),0)
FROM `ibf_ccs_custom_database_2` WHERE `field_60` <> ''
If you want to handle the labeling and formatting in the application layer, you could just return the counts in a single row:
SELECT t1.cnt AS t1_cnt
, t2.cnt AS t2_cnt
, t3.cnt AS t3_cnt
, f1.cnt AS f1_cnt
, f2.cnt AS f2_cnt
FROM ( SELECT COUNT(1) AS cnt FROM `ibf_ccs_custom_database_1` ) t1
JOIN ( SELECT COUNT(1) AS cnt FROM `ibf_ccs_custom_database_2` ) t2
JOIN ( SELECT COUNT(1) AS cnt FROM `ibf_ccs_custom_database_3` ) t3
JOIN ( SELECT COUNT(1) AS cnt FROM `ibf_ccs_custom_database_1` WHERE `field_30` <> '' ) f1
JOIN ( SELECT COUNT(1) AS cnt FROM `ibf_ccs_custom_database_1` WHERE `field_60` <> '' ) f2
I'd just build an array with key => value pairs for your row counts. Something like this:
$db_queries = array();
$query = "SELECT * FROM ibf_ccs_custom_database_1";
$res = $mysqli->query($query);
$db_queries['q1'] = $res->num_rows;
$query = "SELECT * FROM ibf_ccs_custom_database_2";
$res = $mysqli->query($query);
$db_queries['q2'] = $res->num_rows;
$query = "SELECT * FROM ibf_ccs_custom_database_3";
$res = $mysqli->query($query);
$db_queries['q3'] = $res->num_rows;
$query = "SELECT * FROM ibf_ccs_custom_database_1 WHERE field_30 <> ''";
$res = $mysqli->query($query);
$db_queries['q4'] = $res->num_rows;
$query = "SELECT * FROM ibf_ccs_custom_database_2 WHERE field_60 <> ''";
$res = $mysqli->query($query);
$db_queries['q5'] = $res->num_rows;
foreach($db_queries as $k => $v){
echo $k, " contains ", $v, " rows.";
}