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
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
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.
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!
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;');
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')
)