GROUP BY with value of column SQL - mysql

I would like to display for a given user percentages by type (M or C)
I tried with this query:
SELECT TYPE,user_name,pourcentage_c,pourcentage_m,id_user,
COUNT(TYPE) AS crtitique ,COUNT(TYPE) AS majeur
FROM stat_alert
LEFT OUTER JOIN user_qdf ON user_qdf.`id` = id_user
WHERE user_qdf.`id`=id_user AND TYPE='C'
GROUP BY id_user
UNION ALL
SELECT TYPE,user_name,pourcentage_c,pourcentage_m,id_user,
COUNT(TYPE) AS crtitique,COUNT(TYPE) AS majeur
FROM stat_alert
LEFT OUTER JOIN user_qdf ON user_qdf.`id` = id_user
WHERE user_qdf.`id`=id_user AND TYPE='M'
GROUP BY id_user
I want to display in the same line the percentage and the number of critical and important for a single user

You should be able to use something similar to this (this is basically a PIVOT function). This performs a JOIN on your user_qdf table once. You then apply an aggregate function with a CASE statement to get the count (using SUM) for each of the different TYPE values:
SELECT TYPE,
user_name,
pourcentage_c,
pourcentage_m,
id_user,
sum(case when TYPE ='C' then 1 else 0 end) AS crtitique,
sum(case when TYPE ='M' then 1 else 0 end) AS majeur
FROM stat_alert
LEFT OUTER JOIN user_qdf
ON user_qdf.`id` = id_user
WHERE user_qdf.`id`=id_user
AND type IN ('M', 'C')
GROUP BY id_user

This should work (can't test it right now)
SELECT TYPE,user_name,pourcentage_c,pourcentage_m,id_user,
SUM(CASE user_qdf_c.TYPE WHEN 'C' 1 ELSE 0 END) AS crtitique,
SUM(CASE user_qdf_m.TYPE WHEN 'M' 1 ELSE 0 END) AS majeur
FROM stat_alert
LEFT OUTER JOIN user_qdf user_qdf_c ON user_qdf_c.`id` = id_user AND TYPE='C'
LEFT OUTER JOIN user_qdf user_qdf_m ON user_qdf_m.`id` = id_user AND TYPE='M'
GROUP BY id_user
The idea is not to use COUNT, but use SUM instead.
EDIT this can be rewritten much better, with using just one LEFT JOIN (which means a lot better performance!) see #SashiKant's, and #bluefeet's solution.

Try this::
SELECT
TYPE,
user_name,pourcentage_c,pourcentage_m,id_user,COUNT(TYPE) AS crtitique ,COUNT(TYPE) AS majeur
FROM stat_alert
LEFT OUTER JOIN user_qdf ON user_qdf.id = id_user and user_qdf.id=id_user TYPE='C' or TYPE='M' GROUP BY id_user

Try this:
SELECT user_name, pourcentage_c, pourcentage_m, id_user,
COUNT(1) AS crtitique, COUNT(1) AS majeur
FROM stat_alert
LEFT OUTER JOIN user_qdf ON user_qdf.id = id_user
GROUP BY id_user, TYPE

Related

How to get different status count by product id

I have got the result form a complex query below
SELECT o_items.sku,
o_items.name AS 'title',
o_items.qty_ordered AS 'quantity',
s_orders.base_amount_paid AS 'paid/unpaid'
FROM sales_order_payment s_orders
INNER JOIN (SELECT s.sku, s.name, s.qty_ordered, s.order_id
FROM sales_order_item s
INNER JOIN (SELECT p.entity_id
FROM catalog_product_entity AS p
INNER JOIN catalog_product_entity_int AS a
ON p.row_id = a.row_id
WHERE VALUE >= 0
AND
a.attribute_id =
(SELECT attribute_id
FROM eav_attribute
WHERE attribute_code = 'is_darkhorse')) as q
ON s.product_id = q.entity_id
WHERE s.created_at BETWEEN '2019-01-14' AND '2019-01-16') o_items
ON
s_orders.parent_id = o_items.order_id
this is the order data those have been paid or not paid yet. Amount is representing paid and Null representing unpaid status
I am trying to generate below result but couldn't succeed and need help. Actually this result is showing how may quantity of a product has been paid and how many not paid yet. This would be result of above fetched data.
Please guide me how can i proceed to achieve these result.
Use this. ... represent existing code.
select .... , sum(case when s_orders.base_amount_paid is not null
then o_items.qty_ordered
else 0
end) as paid,
sum(case when s_orders.base_amount_paid is null
then o_items.qty_ordered
else 0
end) as unpaid
From .......
You can use if and ifnull functions together(presuming you're using mysql as DBMS)
and GROUP BY expression
SELECT c.sku, c.name,
sum(if(ifnull(base_amount_paid,0)=0,0,1)) as paid,
sum(if(ifnull(qty_ordered,0)=0,0,1)) as unpaid
FROM catalog_prod_ent_derived c
GROUP BY c.sku, c.name
where catalog_prod_ent_derived represents your whole query as a subquery.

shows mysql records twice because of inner joining

In below query (Mentors) are 13 which shows me 26, while (SchoolSupervisor) are 5 which shows me 10 which is wrong. it is because of the Evidence which having 2 evidance, because of 2 evidence the Mentors & SchoolSupervisor values shows me double.
please help me out.
Query:
select t.c_id,t.province,t.district,t.cohort,t.duration,t.venue,t.v_date,t.review_level, t.activity,
SUM(CASE WHEN pr.p_association = "Mentor" THEN 1 ELSE 0 END) as Mentor,
SUM(CASE WHEN pr.p_association = "School Supervisor" THEN 1 ELSE 0 END) as SchoolSupervisor,
(CASE WHEN count(file_id) > 0 THEN "Yes" ELSE "No" END) as evidence
FROM review_m t , review_attndnce ra
LEFT JOIN participant_registration AS pr ON pr.p_id = ra.p_id
LEFT JOIN review_files AS rf ON rf.training_id = ra.c_id
WHERE 1=1 AND t.c_id = ra.c_id
group by t.c_id, ra.c_id order by t.c_id desc
enter image description here
You may perform the aggregations in a separate subquery, and then join to it:
SELECT
t.c_id,
t.province,
t.district,
t.cohort,
t.duration,
t.venue,
t.v_date,
t.review_level,
t.activity,
pr.Mentor,
pr.SchoolSupervisor,
rf.evidence
FROM review_m t
INNER JOIN review_attndnce ra
ON t.c_id = ra.c_id
LEFT JOIN
(
SELECT
p_id,
COUNT(CASE WHEN p_association = 'Mentor' THEN 1 END) AS Mentor,
COUNT(CASE WHEN p_association = 'School Supervisor' THEN 1 END) AS SchoolSupervisor,
FROM participant_registration
GROUP BY p_id
) pr
ON pr.p_id = ra.p_id
LEFT JOIN
(
SELECT
training_id,
CASE WHEN COUNT(file_id) > 0 THEN 'Yes' ELSE 'No' END AS evidence
FROM review_files
GROUP BY training_id
) rf
ON rf.training_id = ra.c_id
ORDER BY
t.c_id DESC;
Note that this also fixes another problem your query had, which was that you were selecting many columns which did not appear in the GROUP BY clause. Under this refactor, there is nothing wrong with your current select, because the aggregation take place in a separate subquery.
try adding this to the WHERE part of your query
AND pr.p_id IS NOT NULL AND rf.training_id IS NOT NULL
You can add a group by pr.p_id to remove the duplicate records there. Since, the group by on pr is not present as of now, there might be multiple records of same p_id for same ra
group by t.c_id, ra.c_id, pr.p_id order by t.c_id desc

Where clause inside joined select

I'm trying to accommodate a similar solution to this one - what I have is a SELECT query inside a JOIN, and the problem is that the query runs at full for all rows (I'm talking 60,000 rows per table - and it runs on 3 tables!).
So what I want to do, is add a WHERE clause to the SELECTs inside the JOIN.
But, I can't access the outer SELECT and get the proper WHERE condition I need.
The query I'm attempting is here:
SELECT c.compete_id AS id,
s.id AS store_id,
c.enabled AS enabled,
s.store_name AS store_name,
s.store_url AS store_url,
c.verified AS verified,
r.rating_total AS rating,
r.positive_percent AS percent,
r.type AS type
FROM compete_settings c
LEFT JOIN stores s
ON c.compete_id = s.id
LEFT JOIN (
(SELECT store_id, rating_total, positive_percent, 'ebay' AS type FROM ebay_sellers WHERE store_id = c.compete_id)
UNION
(SELECT store_id, rating_total, positive_percent, 'amazon' AS type FROM amazon_sellers WHERE store_id = c.compete_id)
UNION
(SELECT store_id, CASE WHEN rank = 0 THEN NULL ELSE (200000 - rank) END AS rating_total, '100' as positive_percent, 'alexa' AS type FROM alexa_ratings WHERE store_id = c.compete_id)
) AS r
ON c.compete_id = r.store_id
WHERE c.store_id = :store_id
Note, :store_id is a variable bound through the framework - let's imagine it's the number 12345.
How can I do this? Any ideas?
We ended up going witha different approach - we just JOINed everything and only selected the right columns with a CASE. Here's the final query:
SELECT c.id AS id,
s.id AS store_id,
c.enabled AS enabled,
s.store_name AS store_name,
s.store_url AS store_url,
c.verified AS verified,
(CASE WHEN eb.rating_total IS NOT NULL THEN eb.rating_total
WHEN am.rating_total IS NOT NULL THEN am.rating_total
WHEN ax.rank IS NOT NULL THEN ax.rank
END) AS rating,
(CASE WHEN eb.positive_percent IS NOT NULL THEN eb.positive_percent
WHEN am.positive_percent IS NOT NULL THEN am.positive_percent
WHEN ax.rank IS NOT NULL THEN '100'
END) AS percent,
(CASE WHEN eb.positive_percent IS NOT NULL THEN 'ebay'
WHEN am.positive_percent IS NOT NULL THEN 'amazon'
WHEN ax.rank IS NOT NULL THEN 'alexa'
END) AS type
FROM compete_settings c
LEFT JOIN stores s
ON c.compete_id = s.id
LEFT JOIN ebay_sellers eb ON c.compete_id = eb.store_id
LEFT JOIN amazon_sellers am ON c.compete_id = am.store_id
LEFT JOIN alexa_ratings ax ON c.compete_id = ax.store_id
WHERE c.store_id = :store_id

MySQL sum of column value from derived table

This is my query:
SELECT usr.id,
count(DISTINCT sol.id) as 'Asked',
count(DISTINCT ans.id) as 'Answered',
sum(DISTINCT CASE ans.accepted WHEN 1 THEN 1 ELSE 0 end) as 'Accepted'
FROM tbl_users usr
LEFT JOIN tbl_solutions sol on sol.authorID = usr.id
LEFT JOIN tbl_solution_answers ans on ans.authorID = usr.id
group by usr.id, sol.authorID
My above query with the sum(DISTINCT CASE ans.accepted WHEN 1 THEN 1 ELSE 0 end) only ever returns 1 though I know that's not the case. I've tried adding a group clause on the ans.authorID but it has no effect.
How can I get the sum of all rows from the tbl_solution_answers ans table where the authorID is that of tbl_users.id and Accepted is 1.
SELECT usr.id,
count(DISTINCT sol.id) as 'Asked',
count(DISTINCT ans.id) as 'Answered',
count(DISTINCT case ans.accepted when 1 then ans.id end) as 'Accepted'
FROM tbl_users usr
LEFT JOIN tbl_solutions sol on sol.authorID = usr.id
LEFT JOIN tbl_solution_answers ans on ans.authorID = usr.id
group by usr.id, sol.authorID, ans.authorID
After so many permutations count(DISTINCT case ans.accepted when 1 then ans.id end) as 'Accepted' seems to work. Now if an authorID in tbl_solution_answers has 8 rows they'll all be returned as Answered and if say 3 of them are Accepted then 3 is returned as Accepted.

Mysql COUNT result rows for a related table

I have
users
------------------------
id | name | other_stuff.....
.
engagement
------------------------
user_id | type_code |
type_code is a varchar, but either A, B, C or NULL
[ EDIT for clarity: Users can have many engagements of each type code. SO I want to count how many they have of each. ]
I want to return ALL user rows, but with a count of A, B and C type engagements. E.g.
users_result
------------------------
user_id | user_name | other_stuff..... | count_A | count_B | count_C |
I've done quite a bit of searching, but found the following issues with other solutions:
The "other_stuff..." is actually grouped / concatenated results from a dozen other joins, so it's a bit of a monster already. So I need to be able to just add the additional fields to the pre-existing "SELECT ...... FROM users..." query.
The three additional required bits of data all come from the same engagement table, each with their own condition. I havent found anything to allow me to use the three conditions on the same related table.
Thanks
[edit]
I tried to simplify the question so people didn't have to look through loads of unnecessary stuff, but seems I might not have given enough info. Here is 'most' of the original query. I've taken out a lot of the selected fields as there are loads, but I've left most of the joins in so you can see basically what is actually going on.
SELECT
user.id,
user.first_name,
user.second_name,
GROUP_CONCAT(DISTINCT illness.id ORDER BY illness.id SEPARATOR ',' ) AS reason_for_treatment,
IF(ww_id=1000003, 1,'') as user_refused_program,
Group_CONCAT(DISTINCT physical_activity.name SEPARATOR ', ') AS programme_options,
COUNT(CASE WHEN engagement_item.type_code LIKE 'wm6%' THEN 1 ELSE NULL END) as count_A,
COUNT(CASE WHEN engagement_item.type_code LIKE 'wm12%' THEN 1 ELSE NULL END) as count_B,
COUNT(CASE WHEN engagement_item.type_code LIKE 'wm6%' THEN 1 ELSE NULL END) as count_C
FROM `user`
LEFT JOIN session AS session_induction ON (user.id = session_induction.user_id AND session_induction.session_type_id = 3)
LEFT JOIN stats AS stats_induction ON session_induction.id = stats_induction.session_id
LEFT JOIN session AS session_interim ON (user.id = session_interim.user_id AND session_interim.session_type_id = 4)
LEFT JOIN stats AS stats_interim ON session_interim.id = stats_interim.session_id
LEFT JOIN session AS session_final ON (user.id = session_final.user_id AND session_final.session_type_id = 5)
LEFT JOIN stats AS stats_final ON session_final.id = stats_final.session_id
LEFT JOIN user_has_illness ON user.ID = user_has_illness.user_id
LEFT JOIN illness ON user_has_illness.illness_id = illness.id
LEFT JOIN user_has_physical_activity ON user.ID = user_has_physical_activity.user_id
LEFT JOIN physical_activity ON user_has_physical_activity.physical_activity_id = physical_activity.id
LEFT JOIN engagement_item ON user.ID = engagement_item.user_ID
WHERE (user.INDUCTION_DATE>='2010-06-09' AND user.INDUCTION_DATE<='2011-06-09' AND user.archive!='1' )
GROUP BY user.id, engagement_item.user_id
It's worth mentioning that it works fine - returns all users with all details required. Except for the count_A B and C cols.
[edit added slightly more simplified query below]
Stripped out the unrelated joins and selects.
SELECT
user.id,
user.first_name,
COUNT(CASE WHEN engagement_item.type_code LIKE 'wm6%' THEN 1 ELSE NULL END) as count_A,
COUNT(CASE WHEN engagement_item.type_code LIKE 'wm12%' THEN 1 ELSE NULL END) as count_B,
COUNT(CASE WHEN engagement_item.type_code LIKE 'wm6%' THEN 1 ELSE NULL END) as count_C
FROM `user`
LEFT JOIN engagement_item ON user.ID = engagement_item.user_ID
GROUP BY user.id, engagement_item.user_id
SELECT e.user_id, u.name,
COUNT(CASE type_code WHEN 'A' THEN 1 ELSE NULL END) as count_A,
COUNT(CASE type_code WHEN 'B' THEN 1 ELSE NULL END) as count_B,
COUNT(CASE type_code WHEN 'C' THEN 1 ELSE NULL END) as count_C
FROM engagement e join users u on (e.user_id = u.id)
GROUP BY e.user_id, u.name
I would use COUNT instead of SUM just because that is what it is made for, counting things when not NULL.
SELECT
user.id,
user.first_name,
user.second_name,
GROUP_CONCAT(DISTINCT illness.id ORDER BY illness.id SEPARATOR ',' ) AS reason_for_treatment,
IF(ww_id=1000003, 1,'') as user_refused_program,
Group_CONCAT(DISTINCT physical_activity.name SEPARATOR ', ') AS programme_options,
ei.count_A, ei.count_B, ei.count_C
FROM `user`
LEFT JOIN ( SELECT user_id
, COUNT(CASE WHEN engagement_item.type_code LIKE 'wm6%' THEN 1 ELSE NULL END) as count_A
, COUNT(CASE WHEN engagement_item.type_code LIKE 'wm12%' THEN 1 ELSE NULL END) as count_B
, COUNT(CASE WHEN engagement_item.type_code LIKE 'wm6%' THEN 1 ELSE NULL END) as count_C
FROM engagement_item
GROUP BY userid ) ei
LEFT JOIN session AS session_induction ON (user.id = session_induction.user_id AND session_induction.session_type_id = 3)
LEFT JOIN stats AS stats_induction ON session_induction.id = stats_induction.session_id
LEFT JOIN session AS session_interim ON (user.id = session_interim.user_id AND session_interim.session_type_id = 4)
LEFT JOIN stats AS stats_interim ON session_interim.id = stats_interim.session_id
LEFT JOIN session AS session_final ON (user.id = session_final.user_id AND session_final.session_type_id = 5)
LEFT JOIN stats AS stats_final ON session_final.id = stats_final.session_id
LEFT JOIN user_has_illness ON user.ID = user_has_illness.user_id
LEFT JOIN illness ON user_has_illness.illness_id = illness.id
LEFT JOIN user_has_physical_activity ON user.ID = user_has_physical_activity.user_id
LEFT JOIN physical_activity ON user_has_physical_activity.physical_activity_id = physical_activity.id
LEFT JOIN engagement_item ON user.ID = engagement_item.user_ID
WHERE (user.INDUCTION_DATE>='2010-06-09' AND user.INDUCTION_DATE<='2011-06-09' AND user.archive!='1' )
GROUP BY user.id, engagement_item.user_id, ei.count_A, ei.count_B, ei.count_C
Something like this perhaps?
select e.user_id, u.name,
sum(case e.type_code when 'A' then 1 else 0 end) as count_A,
sum(case e.type_code when 'B' then 1 else 0 end) as count_B,
sum(case e.type_code when 'C' then 1 else 0 end) as count_C
from engagement e join users u on (e.user_id = u.id)
group by e.user_id, u.name
The interesting part is the use of CASE inside the SUM to split the counting into three chunks.