Count SQL Query Assistance - sql

I am learning how to use SQL and can't seem to work out how to do this kind of query.
The data set has a list of male and female school results which are allocated a score.
LearningArea YearLevel AchievementResult Gender NumericalScore
ENGLISH 1 A M 1
ENGLISH 1 C M 3
ENGLISH 1 C F 3
ENGLISH 1 B F 2
I am trying to analyse the data to show the total score for males and the total score for females without having to do 2 queries.
I have tried myself and looked around but I have only been able to do it with 2 queries.
SELECT Count(Score) AS Male
FROM Data
WHERE Gender = "M"
&
SELECT Count(Score) AS Female
FROM Data
WHERE Gender = "F"
All I'm looking for is anyone that knows of a way to make this one query.
Thanks in Advance

SELECT Gender,Count(*) AS Number
FROM Data
GROUP BY Gender
Based upon the text of your question, you may want SUM instead of COUNT but I'm sure you can work it out from here.

you can use case:
SELECT
COUNT(CASE WHEN Gender = "M" then 1 ELSE NULL END) AS "Male",
COUNT(CASE WHEN Gender = "F" then 1 ELSE NULL END) AS "Female"
FROM Data

Related

Learning SQL, trying to figure out how to get a gender count

I'm learning SQL and one of the questions that got thrown at me in my solo study suddenly is to write a script in order to count how many of each gender is in the client table,
Table CLIENT:
ClientID `First Name` `Last Name` Gender ID `Birth Date`
1 Sally Fields 1 03/05/2000
2 Hannah Montana 1 02/20/1989
3 Bart Simpson 2 02/03/1966
4 Michael Jordan 2 05/02/1970
5 Glennon Doyle 1 NULL
6 Lulu Jenkins 1 09/30/2001
Table GENDER:
ID `Gender Description`
1 Female
2 Male
I'm still very new to this so I'm stumped... the furthest I've gotten is:
SELECT
u.GenderID Gender,
COUNT(u.GenderID)
I'm not sure of where to go from there... the stuff I'm studying went from 1-100 really fast and I'm totally lost lol. Any guidance would be much appreciated!
Assuming you want to return the gender name and a count of each, simply join your two tables and aggregate, grouping by the gender description and counting the rows that belong to each grouping:
select g.`Gender Description`, Count(*) GenderCount
from client c
join gender g on g.id = c.GenderId
group by g.`Gender Description`;
Note the use of short meaningful aliases makes the query more compact and easy to read at a glance.
with GenderTable AS
(
SELECT
CASE
WHEN 'Gender ID'=1 Then 'Female'
Else 'Male'
END AS gender
FROM CLIENT
)
SELECT count(gender), gender
FROM GenderTable
GROUP BY gender
;
You can try this too using the Common Table Expression

I need to do a query from 2 tables using count function

The query contains 4 columns: the full name of the doctor, the number of male patients, the number of female patients, and the total number of patients seen by that doctor.
My problem is that I dont know how to count the number of males and females
I am only suppoused to use COUNT, GROUP BY and basic DML (cant use case when)
data in the table PACIENTE
er diagram
data in table medico
This depends on which database you are using specifically. One possible way to write this is:
SELECT
doc_name,
COUNT(CASE WHEN PAT_SEX = 'M' THEN 1 END) males,
COUNT(CASE WHEN PAT_SEX = 'F' THEN 1 END) females
FROM
...
Another common syntax for this is:
COUNT(IF PAT_SEX = 'M' THEN 1 ENDIF)
Some databases support this directly:
COUNTIF(PAT_SEX = 'M')
If you would really like to avoid any kind of conditional, then you could add gender to your groups but then you will have two rows for each doctor:
SELECT
doc_name,
pat_sex,
count(*)
FROM
...
GROUP BY
doc_name,
pat_sex

Query With Multiple Subqueries Efficiency?

I have data that looks like this:
State Sex
---- ---
GA M
GA M
GA F
GA F
GA F
NY M
NY M
NY M
NY M
NY F
NY F
NY F
NY F
NY F
I want the result to be:
one row per state
col1 State
col2 count of Males
col3 count of Females
col4 total count by state
col 5 percent Male by state
The query I am using is:
select t.state State,
M.count Male,
F.count Female,
count(t.state) Total,
CONCAT(ROUND(CAST(M.count as float)/CAST(count(t.state) as float)*100, 2), '%') as calc
from MyTable t
join
(
select state, count(sex) as count
from MyTable where sex ='M'
group by state) M
on t.state = M.state
join (
select state, count(sex) as count
from MyTable where sex ='F'
group by state) F
ON M.state = F.state
group by t.state, m.count, F.count;
The above query works but I am wondering if I did this in the most effecent way.
This was done using SQLServer but I think this should be the same for all RDBMS.
The link is here: http://sqlfiddle.com/#!18/7a969/87
Use conditional aggregation:
select t.state,
sum(case when sex = 'M' then 1 else 0 end) as males,
sum(case when sex = 'F' then 1 else 0 end) as females,
count(*) as total,
avg(case when sex = 'M' then 1.0 else 0 end) as male_ratio
from MyTable t
group by t.state;
I would expect this to be the fastest method in just about any database.
Here is a SQL Fiddle.
You can compute the number of females by subtracting the number of males from the total count per state. This way, only a single join is required:
with r as (select t.state s, count(*) c from testtable t group by t.state)
select r1.s, t1.m males, r1.c - t1.m females, r1.c total, 100*(t1.m/r1.c) m_percent
from r r1
join (select t.state s, t.sex, count(*) m from testtable t group by t.state, t.sex) t1 on r1.s = t1.s where t1.sex = "M";
Output:
state
males
females
total
m_percent
GA
2
3
5
40.0000
NY
4
5
9
44.4444
See demo.
It is not necessary to separate the data out into the tables for Male and Female.
From a performance point of view, it might help improve performance if the sub-queries were able to make optimal use of indexes, but in reality the chances are low that you would be agregating over only indexed values.
For this simple query you could use simple CASE expressions inline to express the Male/Female columns as BIT values, then we can SUM those values in a single aggregation, however that would require you to define the CASE for Male twice, so you could use it in the Male column and the % Male.
Instead of inline CASE we can use CROSS APPLY as a way to resolve calculations against each row once, and allow you to reference the result:
SELECT t.state State,
SUM(Calcs.IsMale) Male,
SUM(Calcs.IsFemale) Female,
COUNT(1) Total,
CONCAT(ROUND(SUM(Calcs.IsMale)/CAST(COUNT(1) as float)*100, 2), '%') as Calc
FROM MyTable t
CROSS APPLY (SELECT
CASE Sex WHEN 'M' THEN 1 END as [IsMale]
,CASE Sex WHEN 'F' THEN 1 END as [IsFemale]
) as calcs
GROUP BY [State]
Is this any more efficient though? In general it should be, this execution plan is much simpler than joining multiple aggregated sets, but its hard to say without a much larger dataset to test it against.
Either way, I would expect this simple CROSS apply version to win as we only have to process the resultset once.
When running the original and the CROSS APPLY on the given dataset and look at the actual execution plans, SQL Sever reports the CROSS APPLY query to be 25% of the relative cost for the batch:
I apologize in advance for posting this as an image, not really sure if there is a better way to have this discussion
This execution plan reports that the Original Query is 3 times the cost of the CROSS APPLY version, probably due to the 3 table scans in the first query, compared to the single table scan in the CROSS APPLY version.

Selecting count by row combinations

I'm strugling with what on the first sight appeared to be simple SQL query :)
So I have following table which has three columns: PlayerId, Gender, Result (all of type integer).
What I'm trying to do, is to select distinct players of gender 2 (male) with number of each results.
There are about 50 possible results, so new table should have 51 columns:
|PlayerId | 1 | 2 | 3 | ... | 50 |
So I would like to see how many times each individual male (gender 2) player got specific result.
*** In case question is still not entirely clear to you: After each game I insert a row with a player ID, gender and result (from 1 - 50) player achieved in that game. Now I'd like to see how many times each player achieved specfic results.
If there are 50 results and you want them in columns, then you are talking about a pivot. I tend to do these with conditional aggregation:
select player,
sum(case when result = 0 then 1 else 0 end) as result_00,
sum(case when result = 1 then 1 else 0 end) as result_01,
. . .
sum(case when result = 50 then 1 else 0 end) as result_50
from t
group by player;
You can choose a particular gender if you like, with where gender = 2. But why not calculate all at the same time?
try
select player, result, count(*)
from your_table
where Gender = 2
group by player, result;
select PleyerId from tablename where result = 'specific result you want' and gender = 2 group by PleyerId
The easiest way is to use pivoting:
;with cte as(Select * from t
Where gender = 2)
Select * from cte
Pivot(count(gender) for result in([1],[2],[3],....,[50]))p
Fiddle http://sqlfiddle.com/#!3/8dad5/3
One note: keeping gender in scores table is a bad idea. Better make a separate table for players and keep gender there.

SQL Count Expressions

I am trying to create a table to will count the occurrences of each position for various offices.
So if my data is as follows:
Office Position
A Manager
A Supervisor
A Entry Level
A Entry Level
B Manager
B Entry Level
I would want my code to return:
Office Managers Supervisors EntryLevel
A 1 1 2
B 1 0 1
I have my code below. The issue is that this code counts the total amount of occurrences, not the unique count to each office. The results are as follows
A 2 1 3
B 2 1 3
CREATE TABLE OfficeTest AS
SELECT DISTINCT Office,
(Select COUNT(Position) FROM OfficeData WHERE Make_Name = 'Manager') as Managers,
(Select COUNT(Position) FROM OfficeData WHERE Make_Name = 'Supervisor') as Supervisors,
(Select COUNT(Position) FROM OfficeData WHERE Make_Name = 'Entry Level') as EntryLevel
FROM OfficeData
GROUP BY Office;
Any ideas on how to fix this?
The easiest way I can think of doing this is like this:
SELECT Office,
COUNT(CASE WHEN Make_Name = 'Manager' THEN Position END) AS Managers,
COUNT(CASE WHEN Make_Name = 'Supervisor' THEN Position END) AS Supervisors,
COUNT(CASE WHEN Make_Name = 'Entry Level' THEN Position END) AS EntryLevel
FROM OfficeData
GROUP BY Office
COUNT ignores MISSING values; if the Position is not the one specified in the CASE clause, it will return a MISSING value and won't be counted. This way each case considers only the value of Position you compare.
Another option, as stated in the comments, would be pivoting the table. The SAS equivalent is the TRANSPOSE procedure. I don't have a SAS system to create and test a query using it, but here's the documentation in case you want to check it out.
Just to flush out Danny's comment a bit, the SUM code would look like:
proc sql;
CREATE TABLE want AS
SELECT office,
SUM( (position='Manager') ) as Managers,
SUM( (position='Supervisor') ) as Supervisors,
SUM( (position='Entry Level') ) as EntryLevel
FROM OfficeData
GROUP BY office
;quit;
The (position='Manager') bit resolves to 0 or 1, depending on if its true for the current record. I find the SUM version a lot more concise and legible, but both should work for your situation. Plus, its easily extensible to more than one criteria, like (postion='Manager')*(sex='F') to count only female managers.
SUM with CASE statement should resolve the issue. Below is a reference code
proc sql;
create table result as
select age
, sum(case sex when 'F' then 1 else 0 end) as Female
, sum(case sex when 'M' then 1 else 0 end) as Male
from sashelp.class
group by age;
quit;
proc print data=result;run;