Combining results before grouping in SQL - sql

I want to work out the male/female split of my customer based on the person's title (Mr, Mrs, etc)
To do this I need to combine the result for the Miss/Mrs/Ms into a 'female' field.
The query below gets the totals per title but I think I need a sub query to return the combined female figure.
Any help would be greatly appreciated.
Query:
SELECT c.Title, COUNT(*) as Count
FROM
Customers c
GROUP BY Title
ORDER By [Count] DESC
Answer:
Mr 1903
Miss 864
Mrs 488
Ms 108

You could do it like this
SELECT
[Gender] = CASE [Title] WHEN 'Mr' THEN 'M' ELSE 'F' END,
COUNT(*) as Count
FROM
Customers c
GROUP BY
CASE [Title] WHEN 'Mr' THEN 'M' ELSE 'F' END
ORDER By
[Count] DESC
Demo at http://sqlfiddle.com/#!3/05c74/4

You can use CASE to project the new groups for the Titles:
SELECT SUM(CASE WHEN Title IN ('Mr') THEN 1 ELSE 0 END) AS Male,
SUM(CASE WHEN Title IN ('Miss', 'Ms', 'Mrs') THEN 1 ELSE 0 END) AS Female
FROM
Customers c;

Try this:
SELECT (CASE WHEN c.Title = 'Mr' THEN 'Male'
WHEN c.Title IN ('Mrs', 'Miss', 'Ms') THEN 'Female'
ELSE 'NA'
END) AS title,
COUNT(1) AS PeopleCount
FROM Customers c
GROUP BY (CASE WHEN c.Title = 'Mr' THEN 'Male'
WHEN c.Title IN ('Mrs', 'Miss', 'Ms') THEN 'Female'
ELSE 'NA'
END)
ORDER By PeopleCount DESC;

Related

print out the number of two values in the same column

I have a database in which there are people. They have genders. How would I count male and female separate.
SELECT count(id_osb)
from ds_osebe
where spol = 'M'
or spol = 'Z';
this is how i can get the number of male and female combined
I do not know how to make this, it's my second day learning this.
You need to use grouping (group by)
SELECT spol, count(id_osb)
from ds_osebe
where spol = 'M'
or spol = 'Z'
group by spol
NOTE: replace spol if it doesn't denote sex
You can use a CASE expression.
Query
select SUM(case spol when 'M' then 1 else 0 end) as male_cnt
SUM(case spol when 'Z' then 1 else 0 end) as female_cnt
from ds_osebe;
select count(1) over (partition by spol) as qty, spol
from ds_osebe
/*if you have more than 2 gender options*/
where spol in ('Z', 'M');

Is it possible to combine these two sql statements into one statement using group by?

select client_type, count(gender) as num_males
from clients
where gender = 'Male'
group by client_type;
select client_type, count(gender) as num_females
from clients
where gender = 'Female'
group by client_type;
The following SQL statements show the number of males by client type, then the number of females by client type. I would like an SQL statement to show the following columns: client_type, count(gender = 'Male'), count(gender = 'Female'). Is it possible to do this?
You could count a couple of case expressions:
SELECT client_type,
COUNT(CASE gender WHEN 'Male' THEN 1 END) AS num_males,
COUNT(CASE gender WHEN 'Female' THEN 1 END) AS num_females
FROM clients
GROUP BY client_type;

SQL / Postgresql count multiple columns with conditions

I have a simple table of the form:
id
gender
a_feature (bool)
b_feature (bool)
...
xyz_feature (bool)
and I want to sum over all feature columns dependent on gender.
metric
male
female
a_feature
345
3423
b_feature
65
143
...
...
...
xyz_feature
133
5536
Is there a simple way to do this, e.g. using the information_schema.
I found only the solution below, but this is very ugly:
select
'a_feature' as feature_name,
count(case a_feature and gender = 'male') as male,
count(case a_feature and gender = 'female') as female
from table
union
select
b_feature as feature_name,
count(case b_feature and gender = 'male') as male,
count(case b_feature and gender = 'female') as female
from table
.
.
.
select
xyz_feature as feature_name,
count(case xyz_feature and gender = 'male') as male,
count(case xyz_feature and gender = 'female') as female
from table
You can unpivot and aggregate. One method is:
select name,
sum(case when feature and gender = 'male' then 1 else 0 end) as num_male,
sum(case when feature and gender = 'female' then 1 else 0 end) as num_female
from ((select 'a_feature' as name, a_feature as feature, gender
from t
) union all
(select 'b_feature' as name, b_feature, gender
from t
) union all
. . .
) f
group by name;
In Postgres, you would unpivot using a lateral join:
select name,
sum(case when feature and gender = 'male' then 1 else 0 end) as num_male,
sum(case when feature and gender = 'female' then 1 else 0 end) as num_female
from t cross join lateral
(values ('a_feature', a_feature),
('b_feature', b_feature),
. . .
) v(name, feature)
group by name;
You can generate the list for values() using information_schema.columns if you are reluctant to type it all in.
EDIT:
You can construct the values clause using something like this:
select string_agg('(''' || column_name || ''', column_name)', ', ')
from information_schema.columns
where table_name = ?
When you use this in Postgres, what do you mean by t (I think t is for table) in "from t cross join lateral" and what do you mean by v in "v(name, feature)" ?
select name,
sum(case when feature and gender = 'male' then 1 else 0 end) as num_male,
sum(case when feature and gender = 'female' then 1 else 0 end) as num_female
from t cross join lateral
(values ('a_feature', a_feature),
('b_feature', b_feature),
. . .
) v(name, feature)
group by name;

Merge Rows COUNTS in SQL

Currently I have data in sql and need to merge the counts from rows.
My code is:
WHERE lower(GENDER) IS NOT NULL
GROUP BY lower(GENDER)
And it's out putting a table like:
Gender Count
female 100
f 101
male 102
m 103
unknown 104
Is there a way to combine the counts from female and f and then similarly from male and m?
Use a case expression:
select
case lower(gender)
when 'f' then 'female'
when 'm' then 'male'
else lower(gender)
end new_gender,
count(*) cnt
from mytable
where gender is not null
group by case lower(gender)
when 'f' then 'female'
when 'm' then 'male'
else lower(gender)
end
Note that you don't need lower() to check if gender is null.
Some databases support positional parameters in the group by clause, so you can just do:
group by 1
Other databases support re-using aliases defined in the select clause:
group by new_gender
Sure you just need to somehow list how that's going to happen. This can be done using a function or a case statement.
A case statement is a fine solution for a one off query, but I'd recommend a conversion table or a function if you're going to be spreading this across multiple queries.
select
case
when lower(gender) = 'f' or lower(gender) = 'female'
then 'female'
when lower(gender) = 'm' or lower(gender) = 'male'
then 'male'
end as cleanded_gender
, count(*) as gender_count
from
...
where
gender is not null
group by
case
when lower(gender) = 'f' or lower(gender) = 'female'
then 'female'
when lower(gender) = 'm' or lower(gender) = 'male'
then 'male'
end
Depending on your RDBM you can take the substring of Gender, only the first char,
and it will group on "f", "m", "u"... not nice, but it will work

Sql Nested group by

Following query returns a number of people having the same name with gender = Male.
select lookup_name.firstname,count(lookup_name.firstname)
from lookup_name
where gender='M'
group by firstname
similarly, the query below returns a number of people having the same name with gender = Female.
select lookup_name.firstname,count(lookup_name.firstname)
from lookup_name
where gender='F'
group by firstname
I need to write a query which finds out the name and tell the gender (whether male or female) with the greater count. i.e higher probability of that name in the database is of being male or female?
SELECT firstname, Male, Female,
case when Male=Female then 'indeterminate'
when Male>Female then 'probably male'
else 'probably female' end MostProbablySex
FROM (
select firstname,
SUM(case when gender='M' then 1 else 0 end) Male,
SUM(case when gender='F' then 1 else 0 end) Female
from lookup_name
group by firstname
) X;
Or a single pass:
select firstname,
CASE SIGN(2.0 * SUM(case when gender='M' then 1 else 0 end) / COUNT(*) - 1)
WHEN -1 then 'probably female'
WHEN 0 then 'indeterminate'
WHEN 1 then 'probably male'
END
from lookup_name
group by firstname;