SQL Query to change group clause - mysql

I have a query as follows:
SELECT age_groups.Name as name, avg(scores.score) as avg
FROM scores
JOIN users ON scores.id = users.id
JOIN age_groups on user.age_group = age_groups.id;
There are 5 age groups: 0-10, 11-20, 21-30, 31-40, 41-50.
I'd like to have only three age groups in my results: 0-30, 31-40, and 41-50.
What statements would allow me to group three age groups together?
EDIT:
The age_groups table looks like this:
ID - Name
1 - 0-10
2 - 11-20
3 - 21-30
...etc

You can use case to combine age groups. First, though, your query is missing group by, so it should read more like:
SELECT ag.Name as name, avg(s.score) as avg
FROM scores s JOIN
users u
ON s.id = u.id JOIN
age_groups ag
on u.age_group = ag.id
GROUP BY ag.Name;
You can then do what you want as:
SELECT (case when ag.Name in ('0-10', '11-20', '21-30') then '0-30'
else ag.Name
end) as MyAgeGroup, avg(s.score) as avg
FROM scores s JOIN
users u
ON s.id = u.id JOIN
age_groups ag
on u.age_group = ag.id
GROUP BY (case when ag.Name in ('0-10', '11-20', '21-30') then '0-30'
else ag.Name
end);

One solution is to replace "age_groups.Name" in your select clause with a case statement which remaps each of the contributing ranges to your desired range:
CASE age_groups.Name
WHEN '0-10'
THEN '0-30'
WHEN '10-20'
THEN '0-30'
WHEN '20-30'
THEN '0-30'
ELSE age_groups.Name
END AS Name
and also add this to the bottom with a GROUP BY clause (e.g., GROUP BY CASE WHEN ...)
However, if this type of thing might be done frequently, then I would recommend a longer term solution, which is to add numeric bounds to the age groups in your age group table such as Age_Group_Lower_Bound (integer) and Age_Group_Upper_Bound (integer) (e.g., your 0-10 row would then have Age_Group_Lower_Bound=0 and Age_Group_Upper_Bound=10). Once these are in place, your original query could use these, e.g.,:
CASE
WHEN age_groups.Age_Group_Upper_Bound <= 30
THEN '0-30'
ELSE age_Groups.Name
END
Here's an example of that, taking it further with more ranges getting grouped together, e.g.:
CASE
WHEN age_groups.Age_Group_Upper_Bound <= 20
THEN '0-20'
WHEN age_groups.Age_Group_Upper_Bound <= 40
THEN '20-40'
WHEN age_groups.Age_Group_Upper_Bound <= 60
THEN '40-60'
ELSE '60+'
END

Related

Count Case Statement - When One Field Greater Than Another

I'm trying to determine how pervasive a particular mistake is in my database. I'm comparing one field against another, and when that field is greater then the other, I want it to count it. I'm also grouping it by a different statement. The purpose of this query is to determine where there are cases in my data base when one price field is larger then another.
The part of the query that is causing problems is "COUNT(CASE when p.IMAP > p.MSRP = 1 ELSE NULL END)" in the select statement. I put two little stars around it, hoping that'd help highlight where it is.
select b.brandName, b.BrandCode, p.ProductVendorStockNumber, **COUNT(Case When p.IMAP > p.MSRP = 1 ELSE NULL END) as 'Count'**
from products p
join brands b on p.brandID = b.brandID
where b.assignedTo = 'Steve' and p.IMAP > p.MSRP and status = 1
GROUP BY b.BrandName
For the count value You could use sum instead of count adding 1 when the condition is true and 0 when false
In sql for aggregated select the select for columns not in aggregated function and not mentioned in group by is deprecated, in the most recent version of mmysql is not allowed and for the older version the result for these values in unpredicatble so you should in group by then column that you have not in aggregation function in select eg:
select b.brandName
, b.BrandCode
, p.ProductVendorStockNumber
,sum(Case When p.IMAP > p.MSRP THEN 1 ELSE 0 END) as my_count
from products p
join brands b on p.brandID = b.brandID
where b.assignedTo = 'Steve' and p.IMAP > p.MSRP and status = 1
GROUP BY b.BrandName, b.BrandCode, p.ProductVendorStockNumber
or filter the result using the rows without aggregation and a join on the right aggregated rows

Convert query to a Left-Join (?) to include userid's without a match

I am trying use a MySQL query to build a leaderboard of users in a game of Bowl Mania (where users each try to predict the winner of each College Football game, and assign a points weighting to each selection).
I have 3 tables:
--games
id team1 team2 year
--picks
userid gameid points
--actResults
gameid winner
I would like to build a SELECT query that orders each user based on the summer of their "Points" wager for each successfully predicted game (i.e. a successful wager of 5 on gameid=1 earns 5 points, while unsuccessful predictions earn 0).
The query below almost accomplishes what I want, but the only problem is that it does not include users who have zero points (i.e. users who do not have any matches between picks.winners and actResults.winners).
SELECT DISTINCT picks.userid AS Player, SUM( picks.points ) AS Points
FROM picks, games, actResults
WHERE picks.gameid = games.id
AND picks.winner = actResults.winner
AND games.year = '2014'
AND picks.userid = picks.userid
GROUP BY picks.userid
ORDER BY Points DESC
Desired results would look something like this
Player Points
User1 10
User4 9
User3 4
User2 0
User5 0
The hard part is not the conversion to LEFT JOIN (basically that just involves changing the comma-join to a regular ANSI JOIN, and then inserting the keyword LEFT), but rather, fixing the SUM expression to only add the rows where actResults.winner = picks.winner. Here is one way to do it:
SELECT picks.userid AS Player,
SUM(CASE WHEN actResults.winner IS NULL
THEN 0
ELSE picks.points
END) AS Points
FROM picks
JOIN games
ON games.id = picks.gameid
LEFT
JOIN actResults
ON actResults.winner = picks.winner
WHERE games.year = '2014'
GROUP BY picks.userid
ORDER BY Points DESC
;
(Another way is to skip the LEFT JOIN entirely, and use an EXISTS expression with a subquery.)
Edited to add: To add an "Available Points" field, as requested in the comments, you could write:
SELECT picks.userid AS Player,
SUM(CASE WHEN actResults.winner = picks.winner
THEN picks.points
ELSE 0
END) AS Points,
SUM(CASE WHEN actResults.gameid IS NULL
THEN picks.points
ELSE 0
END) AS AvailablePoints
FROM picks
JOIN games
ON games.id = picks.gameid
LEFT
JOIN actResults
ON actResults.gameid = games.id
WHERE games.year = '2014'
GROUP BY picks.userid
ORDER BY Points DESC
;

mysql increment variable using case

I have two tables marks and exams.
In the marks table I have studentid, mark1, mark2 and examid-foreign key from exams for different exams.
I want to get distinct student id and their number of failures in one single query.
The condition for failure is mark1+mark2 <50 or mark1<30. For e.g. If a student having studentid 1 has 15 entries(15 exams) in marks table and the same student failed in 6 so I want to get result as '1' and '6' in two columns and similarly for all students. For this case I wrote query using 'case' and is given below
select
distinct t1.studentid,
(#arrear:=
case
when (t1.mark1+t1.mark2) <50 OR t1.mark1 < 30
then #arrear+1 else #arrear
end) as failures
from marks t1, exams t2,
(select #arrear := 0) r
where t1.examid = t2.examid group by t1.studentid;
But the above query failed to give correct result. How can I modify the query to get correct result?
Try this. You don't need to use variables to help you.
select
m.studentid,
sum(case when m.mark1 + m.mark2 < 50 or m.mark1 < 30 then 1 else 0 end) as failures
from
marks m inner join exams e
on
m.examid = e.examid
group by
m.studentid
The case statement works out if the result is a failure or not and returns 1 for fail, 0 for no fail. Summing the result of this (grouped by studentid) gives you the number of fails per studentid
Oh and the join makes a more efficient join between your two tables :)
You don't need variable #arrear. You can get your info using only query
Try this:
select
distinct t1.studentid,
sum(
case
when (t1.mark1+t1.mark2) <50 OR t1.mark1 < 30
then 1
else 0
end
) as failures
from marks t1, exams t2
where t1.examid = t2.examid group by t1.studentid;

mysql How to get count of a query's resultant rows with conditions on column returned by datediff

The query that I used (on the right of the link) has generated this result (on left).
Please see http://www.sqlfiddle.com/#!2/f34f1/1
I am new to JasperReports and MySql.
I am trying to
count not yet Referred suspects/confirmed clients
count of clients who were referred within 5 days
count of clients who took more than 5 days to get referred.
I am able to get count of clients who were referred within 5 days
SELECT COUNT(*) from
(select p.patient_id,
(CASE WHEN st.smear_result <> 'NEGATIVE' OR st.gxp_result='MTB+'
THEN IF(DATEDIFF(r.date_referred,MIN(st.date_smear_tested)) IS NULL,'N/A',(DATEDIFF(r.date_referred,MIN(st.date_smear_tested))))
ELSE
(CASE WHEN st.smear_result='NEGATIVE' OR st.gxp_result='MTB-'
THEN IF(DATEDIFF(r.date_referred,MAX(st.date_smear_tested)) IS NULL,'N/A',(DATEDIFF(r.date_referred,MAX(st.date_smear_tested))))
ELSE 'N/A' end )END) as days_taken,
IF(r.date_referred IS NULL,'N/A',r.date_referred) date_referred
from patient as p
right outer join sputum_test as st on p.patient_id=st.patient_id
right outer join referral as r on r.patient_id=st.patient_id
where p.suspected_by is not null and (p.patient_status='SUSPECT' or p.patient_status='CONFIRMED')
group by p.patient_id
having days_taken <=5) AS SUBQUERY;
And count of clients who took more than 5 days to get referred.
SELECT COUNT(*) from
(select p.patient_id,
(CASE WHEN st.smear_result <> 'NEGATIVE' OR st.gxp_result='MTB+'
THEN IF(DATEDIFF(r.date_referred,MIN(st.date_smear_tested)) IS NULL,'N/A',(DATEDIFF(r.date_referred,MIN(st.date_smear_tested))))
ELSE
(CASE WHEN st.smear_result='NEGATIVE' OR st.gxp_result='MTB-'
THEN IF(DATEDIFF(r.date_referred,MAX(st.date_smear_tested)) IS NULL,'N/A',(DATEDIFF(r.date_referred,MAX(st.date_smear_tested))))
ELSE 'N/A' end )END) as days_taken,
IF(r.date_referred IS NULL,'N/A',r.date_referred) date_referred
from patient as p
right outer join sputum_test as st on p.patient_id=st.patient_id
right outer join referral as r on r.patient_id=st.patient_id
where p.suspected_by is not null and (p.patient_status='SUSPECT' or p.patient_status='CONFIRMED')
group by p.patient_id
having days_taken > 5) AS SUBQUERY;
But how do I get count not yet Referred suspects/confirmed clients ?
My plan is to somehow get the result as 2 columns:
Column 1 :showing the 3 conditions and Column 2: Showing the sum of rows next to them.
I will pass the solution query in iReport designer to make a pie chart of the 3 conditions as labels and their count showing the percentages for each slice.
Something like this should work:
SELECT SUM(days_taken <= 5) AS within_5_days,
SUM(days_taken > 5) AS more_than_5,
SUM(days_taken IS NULL) as not_yet_referred
FROM (...) AS subquery
Obviously, the subquery should produce NULL for non-referred clients, not N/A as in your original subquery.

Multiple Columns in Case using joins

I have a table "payments" and "payments1". There is a join on both the tables.
Payments:
-----------
id type amount values
1 A 10 x
2 B 20 y
2 A 30 z
I am trying to group by id and type. Such that I can get the results as
id type total_amount type1 total_amount(sum)
-----------------------------------------------
1 A 10
2 A 20 B 30
I have tried following query
select
case when r.type = 'A' then #payment+sum(r.amount) end as total_amount,
case when r.type = 'B' then #refund+sum(r.amount) end as total_amount(sum)
from payments r
But in CASE it gets executed only for one type?
The question is a bit unclear but I assume you're looking for the group by statement.
select
case when r.type = 'A' then #payment+sum(r.amount) end as total_amount,
case when r.type = 'B' then #refund+sum(r.amount) end as total_amount(sum)
FROM payments r
GROUP BY r.type
for each distinct r.type value in the table there will be result row with the aggregation date in the select statement.
Also a different suggestion:
select
(case r.type when 'A' then #payment else #refund end)+sum(r.amount) as total_amount
FROM payments r
GROUP BY r.type
If types are fixed, think you need a query like this:
SELECT
id,
'A' as type,
SUM(CASE WHEN type='A' THEN amount END) sum_typeA,
'B' as type1,
SUM(CASE WHEN type='B' THEN amount END) sum_typeB
FROM
Payments
GROUP BY
id
Please see fiddle here.