MySQL Advanced multidimensional query with CONCAT, JSON and IF statement - php

I working on one complex listing from database and decide to do all possible requests via one query.
Here is working example:
"SELECT
`c`.`categories_id`,
`c`.`categories_status`,
IF(`c`.`categories_status` = 1, 'Active', 'Not Active') AS `categories_status_name`,
TRIM(`cd`.`categories_name`) AS `categories_name`,
TRIM(`cd`.`concert_date`) AS `concert_date`,
TRIM(`cd`.`concert_time`) AS `concert_time`,
TRIM((
SELECT
CONCAT(
'{\"total_quantity\":',
SUM(CASE WHEN `p`.`products_quantity` > 0 THEN `p`.`products_quantity` ELSE 0 END),
',\"total_price\":\"',
SUM(`p`.`products_price`),
'\"}'
)
FROM
`products_to_categories` `ptc`,
`products` `p`
WHERE
`ptc`.`section_id` = `cd`.`section_id`
AND
`p`.`products_id` = `ptc`.`products_id`
)) AS `products_available`,
TRIM((
SELECT
CONCAT(
'{\"total_quantity\":',
SUM(CASE WHEN `op`.`products_quantity` > 0 THEN `op`.`products_quantity` ELSE 0 END),
',\"total_price\":\"',
SUM(`op`.`final_price`),
'\"}'
)
FROM
`products_to_categories` `ptc`,
`orders_products` `op`
WHERE
`ptc`.`section_id` = `cd`.`section_id`
AND
`op`.`products_id` = `ptc`.`products_id`
AND
`op`.`orders_products_status` != 1
)) AS `products_sold`,
TRIM((
SELECT
CONCAT(
'{\"total_quantity\":',
SUM(CASE WHEN `op`.`products_quantity` > 0 THEN `op`.`products_quantity` ELSE 0 END),
',\"total_price\":\"',
SUM(`op`.`final_price`),
'\"}'
)
FROM
`products_to_categories` `ptc`,
`orders_products` `op`
WHERE
`ptc`.`section_id` = `cd`.`section_id`
AND
`op`.`products_id` = `ptc`.`products_id`
AND
`op`.`orders_products_status` = 1
)) AS `products_pending`
FROM
`categories` `c`,
`categories_description` `cd`
WHERE
`c`.`categories_id` = `c`.`section_id`
AND
`cd`.`categories_id` = `c`.`categories_id`
GROUP BY `c`.`categories_id`
ORDER BY `c`.`categories_status` DESC;"
This work great but my main problem is how to do check IF/ELSE or CASE WHEN for custom defined new fields: products_available, products_sold and products_pending?
Problem is that if products not exists inside table orders_products I not get my generated JSON and that's made a small conflict inside PHP.
I can do check in PHP but want to avoid that part and just print JSON like:
{"total_quantity":0,"total_price":"0.0000"}

FROM
`products_to_categories` `ptc`,
`orders_products` `op`
*
WHERE `products_id` =
(SELECT COALESCE(`products_id`,0))
*
AND
`ptc`.`section_id` = `cd`.`section_id`

Related

selecting sql variable returning an empty array

I'm attempting to SET 3 variables in MySQL and get the sum of two of them.
The fist two variables, #cFollow and #cComment, should return an integer value each (the count of how many rows are returned); the third one is the sum of those two integers.
This is my SQL:
SET #cFollow = (SELECT COUNT(*) FROM followers WHERE unix > :unix AND following = :user);
SET #cComment = (SELECT COUNT(*) FROM comments WHERE comment_unix > :unix AND comment_track IN (SELECT upload_id FROM uploads WHERE upload_artist = :user));
SET #total = #cFollow + #cComment;
SELECT #total;
When I tested this on PHPMyAdmin, it returned the correct values and worked perfectly fine. However, when I tested it within PHP, it returned an empty array.
This is my PHP:
$holdPoint = (int)Input::get("hold_point");
$_SQL = "
SET #cFollow = (SELECT COUNT(*) FROM followers WHERE unix > :unix AND following = :user);
SET #cComment = (SELECT COUNT(*) FROM comments WHERE comment_unix > :unix AND comment_track IN (SELECT upload_id FROM uploads WHERE upload_artist = :user));
SET #total = #cFollow + #cComment;
SELECT #total;";
$_PARAMS = [":unix" => $holdPoint, ":user" => $user_id];
$check = DB::getInstance()->queryPro($_SQL, $_PARAMS);
var_dump($check);
This is the result of that var_dump:
array(0){} // not very impressive...
// should be something like int(1) instead
I've been searching around all night learning how to return a variable in PHP from a MySQL query, and this is as far as I've gotten.
All help is appreciated,
Cheers.
This answer is not really meant as a answer but more as a comment.
Also note that your queries
SET #cFollow = (SELECT COUNT(*) FROM followers WHERE unix > :unix AND following = :user);
SET #cComment = (SELECT COUNT(*) FROM comments WHERE comment_unix > :unix AND comment_track IN (SELECT upload_id FROM uploads WHERE upload_artist = :user));
SET #total = #cFollow + #cComment;
SELECT #total;
Can be most likely be rewritten as one query
SELECT
SUM(alias.c) AS total
FROM (
SELECT COUNT(*) AS c FROM followers WHERE unix > :unix AND following = :user
UNION ALL
SELECT COUNT(*) AS c FROM comments WHERE comment_unix > :unix AND comment_track IN (SELECT upload_id FROM uploads WHERE upload_artist = :user)
) AS alias

Make Query More Efficent (Really slow query taking forever!)

What I am trying to do is to get all of the users with the right conditions
so I'm building with a foreach statment a sub_queries to make it work.
the problem is that I got 100,000+ records in the Database.
and this kind of query takes forever to run.
I know I'm not doing it in the best way but I also tried left joins, which was really slow too..
this is the function I'm using:
public function get_affected_users_by_conditions($conditions, $mobile_type)
{
// Basic Query
// Selecting all of the users from `enswitch_mobile users` table
// The total and the `users` with the conditions
$sql = "SELECT COUNT(*) AS `users`,
(SELECT COUNT(*) FROM `enswitch_mobile_users`) AS `total`
FROM
`enswitch_mobile_users` AS `musers`
WHERE
`musers`.`phone_type` = :mobile_type";
$value_counter = 0;
$values = array();
// This is the foreach loop I was talking about
// I am looping all the conditons.
// and when theres a value i'm adding it as a subquery.
foreach($conditions as $cnd) {
switch ($cnd['condition']) {
// REALLY SLOW SUB-QUERY:
case 'talked_atleast':
$value_counter++;
// Here I'm trying to CUT the query by users who talked atleast $value seconds
$sql .= " AND (SELECT SUM(TIME_TO_SEC(TIMEDIFF(`finished_call`,`start_call`))) FROM `enswitch_calls` WHERE `user_id` = `musers`.`id`) >= :value".$value_counter;
$values[$value_counter] = $cnd['value'];
break;
// REALLY SLOW SUB-QUERY:
case 'purchase_atleast':
// Here I am trying to CUT the users by subquery who check if the users has bought at least $value times
$value_counter++;
$sql .= " AND (SELECT COUNT(*) FROM (SELECT user_id FROM enswitch_new_iphone_purchases
UNION
SELECT user_id FROM enswitch_new_android_purchases) AS p WHERE `status` > 0 AND user_id` = `musers`.`id`) >= :value".$value_counter;
$values[$value_counter] = $cnd['value'];
break;
// REALLY SLOW SUB-QUERY:
case 'never_purchase':
// Here I am trying to CUT the users by subquery to get only the users who never made a puchase.
$sql .= ' AND (SELECT COUNT(*) FROM (SELECT user_id FROM enswitch_new_iphone_purchases
UNION
SELECT user_id FROM enswitch_new_android_purchases) AS p WHERE `status` = 0 AND `user_id` = `musers`.`id`) = 0';
break;
}
}
$query = DB::query(Database::SELECT, $sql);
$query->bind(':mobile_type', $mobile_type);
// Looping the values and binding it into the SQL query!
foreach ($values as $k => $v) {
$query->bind(':value'.$k, $values[$k]);
}
// Executing query
$result = $query->execute();
return array('total_users' =>$result[0]['total'], 'affected_users'=>$result[0]['users']);
}
EDIT:
The Slowest Query as Requested: (MySQL)
SELECT COUNT(*) AS `users`,
( SELECT COUNT(*)
FROM `enswitch_mobile_users`
) AS `total`
FROM `enswitch_mobile_users` AS `musers`
WHERE `musers`.`phone_type` = 'iphone'
AND ( SELECT COUNT(*)
FROM ( SELECT `status`,
`user_id`
FROM `enswitch_new_iphone_purchases`
UNION
SELECT `status`,
`user_id`
FROM `enswitch_new_android_purchases`
) AS `p`
WHERE `p`.`status` > 0
AND `p`.`user_id` = `musers`.`id`
) >= 2
The subquery in the second SELECT column will execute for every m_users row that passes the WHERE condition:
SELECT
COUNT(*) AS users,
(SELECT COUNT(*) FROM enswitch_mobile_users) AS total <-- here's the problem
FROM enswitch_mobile_users AS musers
WHERE musers.phone_type = whatever
If I'm reading this correctly, you need a one-row result with the following columns:
users - number of enswitch_mobile_users rows with the specified phone_type
total - count of all enswitch_mobile_users rows
You can get the same result with this query:
SELECT
COUNT(CASE WHEN musers.phone_type = whatever THEN 1 END) AS users,
COUNT(*) AS total
FROM enswitch_mobile_users
The CASE checks for the phone type, and if it matches the one you're interested it it yields a 1, which is counted. If it doesn't match, it yields a NULL, which is not counted.

MYSQL COUNT return NULL?

I have googled my problem but didnt get the answer.
I want to list all of the results of below sql including NULL (when COUNT(review.id) return 0 also) but instead i just got the results of articles of place that only contains review.
$sql = "SELECT tbl_place.id, tbl_place.region_id, tbl_place.subregion_id, tbl_place.title, tbl_place.metalink, tbl_place.img_thumbnail, tbl_place.summary, tbl_place.category1_id, tbl_place.category2_id, tbl_place.category3_id, COUNT(review.id) AS total_review FROM tbl_place
JOIN review ON tbl_place.id = review.place_id
WHERE
tbl_place.category1_id = '32' AND
tbl_place.status = '1' AND
review.rating != '0.00'
GROUP BY tbl_place.id
ORDER BY total_review $by
LIMIT $limit OFFSET $offset";
please use left join for review table instead of join. join is by default inner join so it will take only matched records.
the sql should be :
$sql = "SELECT tbl_place.id,
tbl_place.region_id,
tbl_place.subregion_id,
tbl_place.title,
tbl_place.metalink,
tbl_place.img_thumbnail,
tbl_place.summary,
tbl_place.category1_id,
tbl_place.category2_id,
tbl_place.category3_id,
(SELECT COUNT(*) FROM review WHERE review.rating != '0.00' AND tbl_place.id = review.place_id ) AS total_review
FROM tbl_place WHERE
tbl_place.category1_id = '32' AND
tbl_place.status = '1'
GROUP BY tbl_place.id
ORDER BY total_review $by";
it's working! thx guys!

MySQL query returns rows in mysql but empty set in PHP

The following MySQL query runs in PHP without errors, but the resultset is empty. Directly outputting the query string to a file and running the query in the MySQL client using 'source [filename]' returns several rows of results, as expected.
Is there something that would cause this query not to work with PHP? categorylinks.cl_to and smw_spec2.value_string are both varbinary(255). Show create table indicates engine=InnoDB and default charset=binary.
Things I have tried without success:
$sql = preg_replace("/[\n\t]+/", " ", $sql);
Changing '_wpg' and 'Derp' to CAST('_wpg' AS BINARY(255))
Changing '_wpg' and 'Derp' to BINARY '_wpg'
I am using the MediaWiki DatabaseMysql class to execute the query and fetch rows, but it's a very thin abstraction, and I'm certain it's not the problem (see below).
SELECT
prop.name AS prop_name, prop.count AS prop_count, prop.type AS prop_type,
val.value AS val_value, val.unit AS val_unit, val.count AS val_count
FROM
(
SELECT
s_id, name, type, COUNT(foo.name) AS count
FROM (
(
SELECT
cl.cl_to AS cat_name, s.smw_id AS s_id, s.smw_sortkey AS name, spec.value_string AS type
FROM `smw_ids` s
INNER JOIN (`categorylinks` cl, `page` p, `smw_ids` s2, `smw_atts2` a)
ON (cl.cl_from = p.page_id AND
p.page_title = s2.smw_title AND
s2.smw_id = a.s_id AND
a.p_id = s.smw_id)
LEFT JOIN `smw_spec2` spec ON s.smw_id = spec.s_id
)
UNION ALL
(
SELECT
cl.cl_to AS cat_name, s.smw_id AS s_id, s.smw_sortkey AS name, '_wpg' AS type
FROM `smw_ids` s
INNER JOIN (`categorylinks` cl, `page` p, `smw_ids` s2, `smw_rels2` a)
ON (cl.cl_from = p.page_id AND
p.page_title = s2.smw_title AND
s2.smw_id = a.s_id AND
a.p_id = s.smw_id)
)
) AS foo
WHERE foo.cat_name = 'Derp'
GROUP BY name
ORDER BY count DESC
LIMIT 10
) AS prop
INNER JOIN
(
SELECT
bar.p_id AS p_id, bar.value AS value, bar.unit AS unit, COUNT(bar.value) AS count,
IF( #prev != p_id, #rownum := 1, #rownum := #rownum+1 ) AS rank,
#prev := p_id
FROM (
(SELECT a.p_id AS p_id, a.value_xsd AS value, a.value_unit AS unit FROM `smw_atts2` a)
UNION ALL
(SELECT r.p_id AS p_id, s.smw_sortkey AS value, NULL AS unit
FROM `smw_rels2` r INNER JOIN `smw_ids` s ON r.o_id = s.smw_id)
) AS bar
GROUP BY value, unit
ORDER BY count DESC
) AS val
ON prop.s_id = val.p_id
WHERE val.rank <= 50
ORDER BY prop_count DESC, prop_name, val_count DESC, val_value
Edit: The following test script outputs nothing. query.sql contains exactly the query above, written to file immediately preceding the mysql_query() call in MediaWiki's database class.
$db = mysql_connect('localhost', 'root', '');
mysql_select_db('mediawiki', $db);
$res = mysql_query(file_get_contents("query.sql"), $db);
while ($row = mysql_fetch_assoc($res)) {
var_dump($row);
}
echo mysql_error($db);
Edit: I imported a huge database dump and afterwards, when I loaded the PHP page, there was a noticeable wait that seemed to indicate that the query was running, but still no results showed. I ended up reworking the query, and I no longer have this problem.
Try this to detect and report errors better:
$db = mysql_connect('localhost', 'root', '');
mysql_select_db('mediawiki', $db);
$res = mysql_query(file_get_contents("query.sql"), $db);
if (!$res) {
print "SQL Error ".mysql_errno().":".mysql_error().", from query: '".file_get_contents("query.sql")."'";
} else {
while ($row = mysql_fetch_assoc($res)) {
var_dump($row);
}
}
Try this after mysql_connect:
mysql_query('SET NAMES utf8;');

problem in counting two fields in one query

guys i need to count new private messages and old one from a table
so first thing come to mind is using mysql_num_rows and easy thing to do
// check new pms
$user_id = $userinfo['user_id'];
$sql = "SELECT author_id FROM bb3privmsgs_to WHERE user_id='$user_id' AND (pm_new='1' OR pm_unread='1')";
$result = $db->sql_query($sql) ;
$new_pms = $db->sql_numrows($result);
$db->sql_freeresult($result);
// check old pms
$sql = "SELECT author_id FROM bb3privmsgs_to WHERE user_id='$user_id' AND (pm_new='0' OR pm_unread='0')";
$result = $db->sql_query($sql) ;
$old_pms = $db->sql_numrows($result);
$db->sql_freeresult($result);
but how can i count these two fields just in one statement and shorter lines ?~
Use this query instead:
SELECT SUM(CASE WHEN pm_new = '1' OR pm_unread = '1' THEN 1 ELSE 0 END) AS new_pms,
SUM(CASE WHEN pm_new = '0' OR pm_unread = '0' THEN 1 ELSE 0 END) AS old_pms
FROM bb3privmsgs_to
WHERE user_id='$user_id'
Here's a MySQL-specific version that reads more cleanly:
SELECT COUNT(IF(pm_new = '1' OR pm_unread = '1', 1, NULL)) AS new_pms,
COUNT(IF(pm_new = '0' OR pm_unread = '0', 1, NULL)) AS old_pms
FROM bb3privmsgs_to
WHERE user_id='$user_id'
MySQL will cast comparisons to 1 or 0. You can use SUM() to add up the portion of the WHERE clause you were trying to count results for.
This is a (MySQL specific) shorter alternative to the CASE WHEN examples.
SELECT
SUM(pm_new='1' OR pm_unread='1') as new_pms,
SUM(pm_new='0' OR pm_unread='0') as old_pms
FROM bb3privmsgs_to
WHERE user_id='$userid'
In SQL Server, you can do something like this:
SELECT
SUM(CASE WHEN pm_new='1' OR pm_unread='1' THEN 1 ELSE 0 END),
SUM(CASE WHEN pm_new='0' OR pm_unread='0' THEN 1 ELSE 0 END)
FROM
bb3privmsgs_to WHERE user_id='$user_id'
I'll suppose you can do about the same thing in mySql, let me get back to you on the details...
As a lazy alternative to some of the other suggestions:
SELECT SUM(newPMS) AS newPMS,
SUM(oldPMS) AS oldPMS
FROM ( SELECT COUNT(author_id) AS newPMS,
0 AS oldPMS
FROM bb3privmsgs_to
WHERE user_id='$user_id'
AND (pm_new='1' OR pm_unread='1')
UNION
SELECT 0 AS newPMS
COUNT(author_id) AS oldPMS
FROM bb3privmsgs_to
WHERE user_id='$user_id'
AND (pm_new='0' OR pm_unread='0')
)

Categories