group subquery counts in mysql statement? - php

Wondering if this is possible or not. In the statement below I am getting counts from different tables. This prints our an array of totals for each user_id.
Is there any way to combine these totals so it returns a single array with the totals? I am using subqueries as joins were taking a hit performance-wise so joining is not an option.
$stmt = $db->prepare("
SELECT
(SELECT COUNT(*) FROM log1 WHERE log1.user_id = users.user_id AS l1,
(SELECT COUNT(*) FROM log2 WHERE log2.user_id = users.user_id AS l2,
(SELECT COUNT(*) FROM log3 WHERE log3.user_id = users.user_id AS l3,
(SELECT COUNT(*) FROM log4 WHERE log4.user_id = users.user_id AS l4
FROM computers
INNER JOIN users
ON users.computer_id = computers.computer_id
WHERE computers.account_id = :cw_account_id AND computers.status = :cw_status
");
$binding = array(
'cw_account_id' => $_SESSION['user']['account_id'],
'cw_status' => 1
);
$stmt->execute($binding);
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
At the moment I am doing something like this with the return to get the result I want:
foreach($result as $key)
{
$new['l1'] = $new['l1'] + $key['l1'];
$new['l2'] = $new['l2'] + $key['l2'];
$new['l3'] = $new['l3'] + $key['l3'];
$new['l4'] = $new['l4'] + $key['l4'];
}
return $new;

Use SUM aggregate function:
$stmt = $db->prepare("
SELECT
SUM((SELECT COUNT(*) FROM log1 WHERE log1.user_id = users.user_id)) AS l1,
SUM((SELECT COUNT(*) FROM log2 WHERE log2.user_id = users.user_id)) AS l2,
SUM((SELECT COUNT(*) FROM log3 WHERE log3.user_id = users.user_id)) AS l3,
SUM((SELECT COUNT(*) FROM log4 WHERE log4.user_id = users.user_id)) AS l4
FROM computers
INNER JOIN users
ON users.computer_id = computers.computer_id
WHERE computers.account_id = :cw_account_id AND computers.status = :cw_status
");
This query returns one row with total counts and your required result:
$new = $result[0];

Related

Issue with an mySQL sentence including two temporary table creation

I have a mySQL sentence that works like a charm if I execute it in my phpMyAdmin:
CREATE TEMPORARY TABLE hash1
SELECT * FROM
(
(
SELECT DISTINCT feature_id AS fl, feature_value AS fv FROM gf_product_features WHERE feature_id = '1' AND feature_value = 'No frost total'
) UNION
(
SELECT DISTINCT feature_id AS fl, feature_value AS fv FROM gf_product_features WHERE feature_id = '3' AND feature_value = '43'
)) AS q;
CREATE TEMPORARY TABLE hash2
SELECT * FROM hash1;
SELECT
p.id AS id,
p.main_image AS main_image,
p.type AS taxonomy,
p.name AS model,
p.sku AS sku,
p.price AS price,
b.brand_name AS brand_name,
b.brand_image AS brand_logo,
pf.feature_value AS feature_value,
f.feature AS feature_label,
f.id AS feature_id
FROM
(
SELECT a.*
FROM gf_product AS a
INNER JOIN
(
SELECT product_id
FROM
(
SELECT a.product_id , count(*) AS commons
FROM gf_product_features AS a
INNER JOIN hash1 AS b
ON a.feature_id = b.fl
AND a.feature_value = b.fv
GROUP BY a.product_id
) AS features
WHERE commons = (SELECT count(*) AS count FROM hash2)
) b1 ON a.id = b1.product_id
) AS p
INNER JOIN gf_brands AS b
ON p.brand_id = b.id
INNER JOIN gf_product_features AS pf
ON pf.product_id = p.id
INNER JOIN gf_features AS f
ON pf.feature_id = f.id
ORDER BY price ASC,
feature_id ASC
I want to execute a php function through Ajax request, that constructs dinamically the sql sentence above, but I'm always getting this error in my browser's console:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'CREATE TEMPORARY TABLE hash2
SELECT * FROM hash1;
SELECT
' at line 12
And thus, the following error too:
Warning: mysqli_fetch_assoc() expects parameter 1 to be mysqli_result, boolean given in /www/htdocs/example/inc/functions.php on line 538
Which corresponds to this line of my php code:
while ($row = mysqli_fetch_assoc($result))
Maybe clone hash2 table from hash1 table
CREATE TEMPORARY TABLE hash2
SELECT * FROM hash1;
sounds weird, but if I don't do this in that way, in my phpMyAdmin I get this error:
#1137 - Can't reopen table: 'b'
I can't realize why my sql sentence works fine in my phpMyadmin but, when I construct it on my php file it doesn't works. Can anybody help me, please?
For further information, this is my PHP code:
function getProductsFromFilteredQuery($connection, $filters, &$html)
{
$sql = '';
$m = count($filters); // $filters are an array of values like this: ['value1A, value2A', 'value1B, value2B', ...]
$sql = 'CREATE TEMPORARY TABLE hash1
SELECT * FROM
(';
for ($n = 0; $n < $m; $n++)
{
$string = explode(', ', $filters[$n]);
$feature_id = $string[0];
$feature_value = $string[1];
$sql .= "
(
SELECT DISTINCT feature_id AS fl, feature_value AS fv FROM gf_product_features WHERE feature_id = '" . $feature_id . "' AND feature_value = '" . $feature_value . "'
)";
if ($n < ($m - 1))
{
$sql .= ' UNION ';
}
}
$sql .= ') AS q;
CREATE TEMPORARY TABLE hash2 -- In this line I get an error
SELECT * FROM hash1;
SELECT
p.id AS id,
p.main_image AS main_image,
p.type AS taxonomy,
p.name AS model,
p.sku AS sku,
p.price AS price,
b.brand_name AS brand_name,
b.brand_image AS brand_logo,
pf.feature_value AS feature_value,
f.feature AS feature_label,
f.id AS feature_id
FROM
(
SELECT a.*
FROM gf_product AS a
INNER JOIN
(
SELECT product_id
FROM
(
SELECT a.product_id , count(*) AS commons
FROM gf_product_features AS a
INNER JOIN hash1 AS b
ON a.feature_id = b.fl
AND a.feature_value = b.fv
GROUP BY a.product_id
) AS features
WHERE commons = (SELECT count(*) AS count FROM hash2)
) b1 ON a.id = b1.product_id
) AS p
INNER JOIN gf_brands AS b
ON p.brand_id = b.id
INNER JOIN gf_product_features AS pf
ON pf.product_id = p.id
INNER JOIN gf_features AS f
ON pf.feature_id = f.id
ORDER BY price ASC,
feature_id ASC';
$result = mysqli_query($connection, $sql);
while ($row = mysqli_fetch_assoc($result)) // In this line I get an error too
{
// Do some stuff... and at last, return the resulting $html
}
};
I finally could find the error. In my phpMyAdmin it worked as well because someone can execute several queries in the SQL console. There is no problem with it.
However, when coding an mySQL query through PHP you only can run one mySQL sentence at once. Well, there is an exception: You can use mysqli_multi_query + mysqli_more_results, or something like these. But as I was coded it, you can't.
So there is two options: rewrite the PHP code like described in the pages of the two links above, or doing several mysqli_query within the PHP function.
I decided to do it through the second option, so the working code is the following (Notice the comments after each mysqli_query):
function getProductsFromFilteredQuery($mysqli, $filters, &$html) {
$sql = '';
$m = count($filters);
$sql = 'DROP TEMPORARY TABLE IF EXISTS hash1;';
$result = mysqli_query($mysqli, $sql); // A single query
$sql = 'DROP TEMPORARY TABLE IF EXISTS hash2;';
$result = mysqli_query($mysqli, $sql); // Another single query
$sql = 'CREATE TEMPORARY TABLE hash1
SELECT * FROM
(';
for ($n = 0; $n < $m; $n++)
{
$string = explode(', ', $filters[$n]);
$feature_id = $string[0];
$feature_value = $string[1];
$sql .= "
(SELECT DISTINCT feature_id AS fl, feature_value AS fv FROM gf_product_features WHERE feature_id = '" . $feature_id . "' AND feature_value = '" . $feature_value . "')";
if ($n < ($m - 1))
{
$sql .= ' UNION ';
}
}
$sql .= ') AS q1';
$result = mysqli_query($mysqli, $sql); // Another single query
$sql = 'CREATE TEMPORARY TABLE hash2
SELECT * FROM hash1;';
$result = mysqli_query($mysqli, $sql); // Another single query
$sql = 'SELECT
p.id AS id,
p.main_image AS main_image,
p.type AS taxonomy,
p.name AS model,
p.sku AS sku,
p.price AS price,
b.brand_name AS brand_name,
b.brand_image AS brand_logo,
pf.feature_value AS feature_value,
f.feature AS feature_label,
f.id AS feature_id
FROM
(
SELECT a.*
FROM gf_product AS a
INNER JOIN
(
SELECT product_id
FROM
(
SELECT a.product_id , count(*) AS commons
FROM gf_product_features AS a
INNER JOIN hash1 AS b
ON a.feature_id = b.fl
AND a.feature_value = b.fv
GROUP BY a.product_id
) AS features
WHERE commons = (SELECT count(*) AS count FROM hash2)
) b1 ON a.id = b1.product_id
) AS p
INNER JOIN gf_brands AS b
ON p.brand_id = b.id
INNER JOIN gf_product_features AS pf
ON pf.product_id = p.id
INNER JOIN gf_features AS f
ON pf.feature_id = f.id
ORDER BY price ASC,
feature_id ASC';
$result = mysqli_query($mysqli, $sql); // Another single query. The last one.
while ($row = mysqli_fetch_assoc($result))
{
// My stuff here...
}
}; // #END of function

MYSQL Order By column_name with many table left joins

I have a 4 table merge going on and in the end I want it to be sorted by an "ORDER BY" according to some variable. Right now this is always returning the same order of entries.
Something like:
if(isset($_GET['filter'])){
$filter = $_GET['filter'];
#Example $filter = 'date' or team or game_num
$q = $db->prepare("SELECT g.game_num, s.date, t.team
FROM schedule n
LEFT JOIN g_lkp g
ON n.game_num = g.game_num
LEFT JOIN dates s
ON n.date = s.date
LEFT JOIN teams t
ON n.home_team_nbr = t.team
ORDER BY '$filter' ");
$q->execute();
$qR = $q->fetchAll(PDO::FETCH_ASSOC);
if ($q->rowCount() > 0) {
foreach ($qR as $row) {
echo '
//First check value of $filter
$q = $db->prepare("SELECT g.game_num game_num, s.date date, t.team team
FROM schedule n
LEFT JOIN g_lkp g
ON n.game_num = g.game_num
LEFT JOIN dates s
ON n.date = s.date
LEFT JOIN teams t
ON n.home_team_nbr = t.team
ORDER BY $filter ");

Speeding up SQL loop

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.

MySQL Query Optimization - Sub Queries + Multiple Joins

How can I optimize this query? It takes 2-3 seconds to get 10 rows.
SELECT users.user_id, users.user_name, users.user_display_name,
(SELECT COUNT(tweet_id) FROM tweets WHERE tweet_user_id = users.user_id AND tweet_status = 1) AS user_tweets_count,
(SELECT COUNT(tweet_reply_id) FROM tweets_reply tr JOIN tweets t ON t.tweet_id = tr.tweet_reply_tweet_id WHERE tweet_reply_user_id = users.user_id AND t.tweet_status = 1 AND tr.tweet_reply_status = 1) AS user_replies_count
FROM users
JOIN tweets ON tweets.tweet_user_id = users.user_id
JOIN tweets_reply ON tweets_reply.tweet_reply_user_id = users.user_id
WHERE (tweets_reply.tweet_reply_status = 1 AND tweets.tweet_status = 1)
GROUP BY users.user_id
ORDER BY (user_tweets_count + user_replies_count) DESC
LIMIT 10
SELECT users.user_id, users.user_name, users.user_display_name,
COUNT(tweets.tweet_id) AS user_tweets_count,
COUNT(tweets_reply.tweet_reply_id) AS user_replies_count
FROM users
JOIN tweets ON tweets.tweet_user_id = users.user_id
JOIN tweets_reply ON tweets_reply.tweet_reply_user_id = users.user_id
WHERE (tweets_reply.tweet_reply_status = 1 AND tweets.tweet_status = 1)
GROUP BY users.user_id
ORDER BY (user_tweets_count + user_replies_count) DESC
LIMIT 10
Hope this helps.

Combining 3 mysql queries into 1

I need to query two new table $res_info and $res_text... how can I combine all 3 queries into one. The first query $res grabs everything include the unique field m_id which is used in the two other tables. can this be done with a UNION?
$res = #mysql_query("SELECT *, DATE_FORMAT(m_lld,'%m/%d/%y')
AS m_lld_formatted
FROM social_members
WHERE m_user='$en[user]'");
if (#mysql_num_rows($res) == 0) call404();
$line = #mysql_fetch_assoc($res);
foreach ($line as $key => $value)
$en['m'.$key] = str_replace("\n",'<br/>',stripslashes($value));
$res_info = mysql_query("SELECT *,
FROM social_meminfo
WHERE m_id = '".$en['mm_id']."'");
$res_text = mysql_query("SELECT *,
FROM social_memtext
WHERE m_id = '".$en['mm_id']."'");
Based on your comments I think you are looking for one OUTER JOIN and another INNER JOIN like this:
SELECT sm.*, DATE_FORMAT(sm.m_lld,'%m/%d/%y') AS m_lld_formatted
FROM social_members sm
LEFT OUTER JOIN social_memtext smt ON (sm.m_id = smt.m_id)
JOIN social_meminfo smi ON (sm.m_id = smi.m_id)
WHERE sm.m_user = "$en['user']"
LEFT OUTER JOIN will take care of situation when table social_memtext does have matching entries.
//use mysql_real_escape_string for your $en['User']
SELECT sm.*, DATE_FORMAT(sm.m_lld,'%m/%d/%y')
AS m_lld_formatted
FROM social_members sm
JOIN social_meminfo smi ON(smi.m_id = sm.m_id)
JOIN social_memtext smt ON(smt.m_id = sm.m_id)
WHERE sm.m_user = "$en['user']"

Categories