Double group by - mysql

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)

Related

How to change result row into column in mysql

I have below result set
Name | ID | Total | CityName
--------------------------------
A 1 2 ABC
--------------------------------
B 2 1 XYZ
--------------------------------
C 3 1 ABC
--------------------------------
How I can show below result
Name | ID | ABC | XYZ
---------------------------
A 1 2 0
---------------------------
B 2 0 1
---------------------------
C 3 1 0
---------------------------
Use conditional aggregation
select name, id, max(case when CityName='ABC' then total else 0 end) as ABC
max(case when CityName='XYZ' then total else 0 end) as XYZ
from tablename
group by name,id
Well it seems like a subquery, or procedure would be a good idea.
Something like:
select Name,ID,
(select Total from tets where CityName ='ABC' and Id = t.Id) as 'ABC',
(select Total from tets where CityName ='XYZ' and Id = t.Id) as 'XYZ'
from tets t

SQL check multiple rows with if and get the count

Table:
id | name | country
---------------------
1 | abc | India
2 | abc | America
3 | abc | USA
4 | xyz | India
5 | xyz | America
6 | xyz | USA
QUERY tried so far:
SELECT `id`,
if(`country` = 'India',count(id),0) as object1,
if(`country` = 'America', count(id),0) as object2,
if(`country` = 'USA',count(id),0) as object3
FROM `table`
the above gives me output like this:
id | name | object1 | object2 | object3
---------------------------------------------
1 | India | 6 | 0 | 0
I want output like :
id | name | object1 | object2 | object3
---------------------------------------------
1 | India | 1 | 1 | 1
1 | America | 1 | 1 | 1
1 | USA | 1 | 1 | 1
Please someone help me out to get this output.
I think you want conditional aggregation, but it is unclear what the objects are. Based on the data, I might think:
SELECT country as name,
sum(name = 'abc') as abc,
sum(name = 'xyz') as xyz
FROM `table`
group by country;
Try this:
SELECT `id`,`country`, count(`country`) as 'CountOfCountry'
FROM `table`
GROUP by id,Country
That will give you a count of each unique country, all grouped.
This is assuming that what you are calling "objects" is really the count per country.
I am not quite sure what the third column should be because you don't have the sample data to support it. But how about the following (SQL Fiddle):
SELECT country,
SUM(CASE WHEN name = 'abc' THEN 1 ELSE 0 END) AS object1,
SUM(CASE WHEN name = 'xyz' THEN 1 ELSE 0 END) AS object2,
SUM(CASE WHEN name = 'nunya' THEN 1 ELSE 0 END) AS object3
FROM MyTable
GROUP BY country
I wasn't sure if you wanted to return the ID with your query as all the IDs in your sample output are 1, and I'm not sure how that would represent the sample data you gave, but assuming that was a typo. This is what I came up with. Here is the sql fiddle
http://sqlfiddle.com/#!6/b763d/7
select id,country,
(select count(id) from things where country='india') as object1,
(select count(id) from things where country='america') as object2,
(select count(id) from things where country='usa') as object3
from things
Now if you dont want the ID and just want things to be grouped by country then do the following.
select country,
(select count(id) from things where country='india') as object1,
(select count(id) from things where country='america') as object2,
(select count(id) from things where country='usa') as object3
from things
group by country

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

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

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.