Getting conditional counts on to the same row in MySQL / SQL - mysql

Suppose I have a table, Foo, that looks like this:
ID | Name | Gender | Team
1 | Bob | Male | A
2 | Amy | Female | A
3 | Cat | Female | B
4 | Dave | Male | B
5 | Evan | Male | B
If I wanted to get a list of the number of males and females per team on the same row, how would I do that?
I know I could do SELECT COUNT(Name) as "#", Team, Gender FROM foo GROUP BY Team, Gender, and that's fine for most purpose.
But that would give me 2 rows per team, like below, and that can be a pain.
# Team Gender
1 | A | Male
1 | A | Female
1 | B | Female
2 | B | Male
How could I structure the query such that they appear on the same row?
ie,
Team | Males | Females
A | 1 | 1
B | 2 | 1

select team,
SUM(case when gender='Male' then 1 else 0 end) Male,
SUM(case when gender='Female' then 1 else 0 end) Female
from tbl
group by team
For the comment
Imagine now that each row has an arbitrary numeric score associated with it, in a Score column. How would I have 'Male points' and 'Female points? Would that be SUM(case when gender="male" then select points else 0 end) "Male Points"
You're close. The answer is
select team,
SUM(case when gender='Male' then 1 else 0 end) Male,
SUM(case when gender='Male' then points else 0 end) `Male Points`,
SUM(case when gender='Female' then 1 else 0 end) Female,
SUM(case when gender='Female' then points else 0 end) `Female Points`
from tbl
group by team

The pattern I was looking for was a Self-Join; the syntax and logic is, in my mind, more elegant then the CASE pattern.
Specifically,
SELECT Males.Team, COUNT(Males.id) as "Males", SUM(Males.Points) as "Male Points",
COUNT(Females.id) as "Females", SUM(Females.Points) as "Female Points"
FROM scores as Males
LEFT JOIN scores as Females ON Males.Team=Females.Team AND Females.Gender="Female"
WHERE Males.Gender="Male"
GROUP BY Team
Instead of case statements, the groupings I want in different columns get split into their own copies of the same table. You then join the table of Male players with a Table of Female players on the Team, and then group by the Team.

Related

MySQL Sum operation based on other column

I have a table like this:
Date | Name | Pick | Amount
-----------+-------+-------+-------
2018-01-01 | Alice | Apple | 2
2018-01-01 | Alice | Grape | 3
2018-01-01 | Bob | Apple | 4
2018-01-02 | Alice | Apple | 5
2018-01-02 | Bob | Grape | 6
What is the SQL statement that produce result like below?
Name | Apple | Grape | Total
------+-------+-------+------
Alice | 7 | 3 | 10
Bob | 4 | 6 | 10
Using CASE condition, it will be possible.
Try this:
select name
,sum(case when pick = 'Apple' then amount end)Apple
,sum(case when pick = 'Grape' then amount end)Grape
,sum(case when pick = 'Apple' then amount end)
+sum(case when pick = 'Grape' then amount end)Total
from your_table
group by name
You want conditional aggregation :
select name,
sum(case when pick = 'Apple' then amount else 0 end) Apple,
sum(case when pick = 'Grape' then amount else 0 end) Grape,
sum(case when pick in ('Apple', 'Grape') then amount else 0 end) Total
from table t
group by name;
the is also a way to calculate your result, using aggregate function and or
select name,
sum(case when pick = 'Apple' then amount else 0 end) Apple,
sum(case when pick = 'Grape' then amount else 0 end) Grape,
sum(case when pick in('Apple','Grape') then amount else 0 end) Total
from tableA
group by name
http://sqlfiddle.com/#!9/4c56c/4
SELECT Name,
sum(if(Pick='Apple', Amount, 0)) as Apple,
sum(if(Pick='Grape', amount, 0)) as Grape
FROM table_name
GROUP BY Name;
You can use nested queries. One to calculate the grapes and the apples and one to calculate the total. The advantage is that you have the logic for calculation of each column on a single place and the calculation of the total is a bit more visible
SELECT
Name,
Apple,
Grape,
Apple + Grape as Total
FROM (
SELECT
`name` as Name,
SUM(IF(pick = 'Apple', amount, 0)) as Apple,
SUM(IF(pick = 'Grape', amount, 0)) as Grape
FROM test
GROUP BY name
) AS t1
db-fiddle

SQL Count on Multiple Fields

I'm not even sure if this is possible or whether I just need to write multiple queries.
I have a database table as follows:-
USER
university_id
gender
The output I'd like to achieve is as follows
| Uni | Total Users | Male | Female
| Uni1 | 30 | 15 | 15
| Uni2 | 40 | 25 | 15
I would be grateful if you could let me know if this is possible to achieve in a single queries, or whether I should just use different queries for each result.
Thank you in advance.
Phill
Here's how you can do it in a single query:
select
UniversityId,
count(*) as `Total Users`,
sum(case when gender = 'male' then 1 else 0 end) as Male,
sum(case when gender = 'female' then 1 else 0 end) as Female
from User
group by UniversityId
Demo: http://www.sqlfiddle.com/#!2/e7d16a/4

Outputting four rows based on two categories each with a boolean

If I have a table of users:
USER ID | GENDER | VOTE
----------------------------------
1 Male Yes
2 Female No
3 Male No
4 Male Yes
5 Female Yes
How can I write a query that outputs the genders by their vote breakdown:
GENDER | YES | NO
----------------------------------
Male 2 1
Female 1 1
Is this possible to do it in SQL or should I use PHP to build these totals?
Any help is appreciated! :)
This type of transformation of data is known as pivot. MySQL doesn't have a PIVOT function but you can replicate the function by using an aggregate function with a CASE expression:
select gender,
sum(case when vote = 'Yes' then 1 else 0 end) Yes,
sum(case when vote = 'No' then 1 else 0 end) No
from yourtable
group by gender;
See SQL Fiddle with Demo. Since you are using MySQL you could also use the IF() control-flow operator:
select gender,
sum(if(vote = 'Yes', 1, 0)) Yes,
sum(if(vote = 'No', 1, 0)) No
from yourtable
group by gender;
See SQL Fiddle with Demo. These both give a result:
| GENDER | YES | NO |
|--------|-----|----|
| Female | 1 | 1 |
| Male | 2 | 1 |

MySQL multi join / group query

idI have a table like this
|----|-----------|--------|
| ID | Ethnicity | Gender |
| 1 | 2 | 1 |
| 2 | 3 | 1 |
| 3 | 4 | 2 |
|----|-----------|--------|
etc ....
And I'm trying to get back a set of results that show me ethnicities group by male(1) and female(2)
So the result row would in this example would be:
Ethnicity Male Female
2 1 0
3 1 0
4 0 1
So far I'm close with to what I want with:
SELECT ethnicity,
(SELECT
count(id)
FROM
table_name
WHERE
gender = '2' ) as female,
(SELECT
count(id)
FROM
table_name
WHERE
gender = '1') as male
FROM table_name
GROUP BY ethnicity
Which gives me:
Ethnicity Male Female
2 2 1
3 2 1
4 2 1
But need the count(id) to only be a count of the adno of that ethnicity row if that makes sense.
Try this instead:
SELECT
ethnicity,
SUM(CASE WHEN gender = 1 THEN 1 ELSE 0 END) AS female,
SUM(CASE WHEN gender = 2 THEN 1 ELSE 0 END) AS Male
FROM students
GROUP BY ethnicity;
The above should work in virtually any SQL product, not just in MySQL. If you like, however, you can also use this version, which employs MySQL's implicit conversion of booleans to ints (true -> 1, false -> 0):
SELECT
ethnicity,
SUM(gender = 1) AS female,
SUM(gender = 2) AS Male
FROM students
GROUP BY ethnicity;
SELECT
ethnicity,
CASE WHEN gender = 1 THEN 1 ELSE 0 END AS Male,
CASE WHEN gender = 2 THEN 1 ELSE 0 END AS Female
FROM students

Double group by

I've got table with two columns: name and grade. It looks sth like this:
NAME | GRADE
Adam | 1
Adam | 2
Adam | 2
Adam | 3
Frank | 2
Frank | 1
Now I want create view that will looks like this:
NAME | GRADE 1 | GRADE 2 | GRADE 3
Adam | 1 | 2 | 1
Frank | 1 | 1 | 0
I've wrote this:
SELECT Name,
(SELECT COUNT(Grade)
FROM dbo.Rodzaj
WHERE Grade = '1') as Grade_1,
(SELECT COUNT(Grade)
FROM dbo.Rodzaj
WHERE Grade = '2) as Grade_2,
(SELECT COUNT(Grade)
FROM dbo.Rodzaj
WHERE Grade = '3') as Grade_3
FROM dbo.Rodzaj
GROUP BY Name
but it doesn't work...
I would appreciate any help
What you are looking for is called a "pivot table" and it is done with a chain of CASE statements which apply a 1 or 0 for each condition, then SUM() up the ones and zeros to retrieve a count.
SELECT
NAME,
SUM(CASE WHEN GRADE = 1 THEN 1 ELSE 0 END) AS GRADE1,
SUM(CASE WHEN GRADE = 2 THEN 1 ELSE 0 END) AS GRADE2,
SUM(CASE WHEN GRADE = 3 THEN 1 ELSE 0 END) AS GRADE3
FROM Rodzaj
GROUP BY NAME
Note that if you need this to have a dynamic number of columns, you will have to construct the query using a scripting language and a loop. (Or a loop inside a stored procedure)