SQL count sum and joining table - sql

I ran into a question from my SQL class but did not have a solution to it. My query needs to show ONLY the movies that have same number of female and male actors.
I have three tables:
(table:field 1,field2):
Casting: actor_number, movie_number
Actor_List: id, name, gender
Movie_List: id, movie_name

The sub-query uses a little CASE() trick to increment counts conditionally (i.e. countif). The sub-query factoring syntax means we only execute the query once.
with cte as (
select m.movie_name
, sum(case when a.gender = 'M' then 1 else 0 end) as male_tot
, sum(case when a.gender = 'F' then 1 else 0 end) as female_tot
from casting c
join movie_list m
on c.movie_number = m.id
join actor_list a
on c.actor_number = a.id
group by m.name
)
select cte.*
from cte
where cte.male_tot = cte.female_tot ;

Related

CASE statement leaving NULL

I am trying to make it so there are no NULLs in the JobTitle column. When I do it without at the CASE, I get two JobTitle columns (one for males and one for females) and some have NULL. I want to make it so there is just one column listing all the job titles then listing the total number of males/females next to that column in their own columns. (This is using the AdventureWorks db)
USE AdventureWorks2019
GO
select count(hre.gender) AS NumberOfFemales, JobTitle
into #FemalesPerJobTitle
from HumanResources.employee as hre
group by JobTitle, Gender
having gender = 'F';
SELECT COUNT(HRE.Gender) AS NumberOfMales, JobTitle
INTO #MalesPerJobTitle
FROM HumanResources.Employee AS HRE
GROUP BY JobTitle, Gender
HAVING gender = 'M';
SELECT FPJ.NumberOfFemales AS Females
, MPJ.NumberOfMales AS Males
,
CASE
WHEN MPJ.JobTitle IS NULL THEN FPJ.JobTitle
END AS JobTitle
FROM #FemalesPerJobTitle AS FPJ
FULL OUTER JOIN #MalesPerJobTitle AS MPJ
ON FPJ.JobTitle = MPJ.JobTitle
SELECT
JobTitle,
SUM(CASE WHEN gender = ‘M’ THEN 1 ELSE 0 END) AS Males,
SUM(CASE WHEN gender = ‘F’ THEN 1 ELSE 0 END) AS Females
FROM HumanResources.Employee
GROUP BY JobTitle
You could probably put a COALESCE around the SUMs if it is returning any nulls and you want zeros instead
It sounds like you want to produce a result set that
list all job titles, with
the count of men and women holding each title
If you have a table holding the complete list of job titles (which the database should since job title is/would seem to be a proper entity), I would use that as the source for the job title column.
But, if you don't have that, I'd do something like the following and use a derived table to give the the distinct set of job titles in both source tables:
select coalesce( f.NumberOfFemales , 0 ) Females ,
coalesce( m.NumberOfMales , 0 ) Males ,
jt.JobTitle JobTitle
from ( select JobTitle from #FemalesPerJobTitle
UNION select JobTitle from #MalesPerJobTitle
) jt
left join #FemalesPerJobTitle f on f.JobTitle = jt.JobTitle
left join #MalesPerJobTitle m on m.JobTitle = jt.JobTitle
Another way to go about it (and probably easier for others to understand) would be to do something like this:
select JobTitle = t.JobTitle,
NumberOfFemales = sum( t.NumberOfFemales ) ,
NumberOfMales = sum( t.NumberOfMales )
from ( select JobTitle,
NumberOfFemales = NumberOfFemales,
NumberOfMales = 0
from #FemalesPerJobTitle
UNION ALL
select JobTitle,
NumberOfFemales = 0 ,
NumberOfMales = NumberOfMales
from #MalesPerJobTitle
) t
group by t.JobTitle
Here, the derived table uses UNION ALL to not eliminate duplicates because (1) there shouldn't be any, (2) the query will be more efficient, and (3) the group by clause will take care of the roll-up.
Here you go, I used CTE which makes more sense than temp tables here.
WITH Fcount AS
(
select count(hre.gender) AS NumberOfFemales, JobTitle
from HumanResources.employee as hre
WHERE gender = 'F'
group by JobTitle
), Mcount AS
(
SELECT COUNT(HRE.Gender) AS NumberOfMales, JobTitle
FROM HumanResources.Employee AS HRE
WHERE gender = 'M'
GROUP BY JobTitle
), titles CASE
(
SELECT DISTINCT JobTitle FROM Fcount
UNION ALL
SELECT DISTINCT JobTitle FROM MCount
)
SELECT titles.JobTitle,
FPJ.NumberOfFemales AS Females,
MPJ.NumberOfMales AS Males
FROM titles
LEFT JOIN Fcount AS FPJ ON titles.JobTitle = FPJ.JobTitle
LEFT JOIN Mcount AS MPJ ON titles.JobTitle = MPJ.JobTitle

SQL Query: Count "id" occurrences in two tables

I have these 3 tables and I am trying to count, how many "hints" and "quizzes" are there for specific town id.
db_town
id
town
1
New York
db_hint
id
town_id
hint
1
1
test
db_quiz
id
town_id
quiz
1
1
quiz 1
2
1
quiz 2
I am using this statement, but it does not work :(
SELECT count(q.id),count(h.id) FROM `db_town` t LEFT JOIN `db_quiz` q ON t.id = q.town_id LEFT JOIN `db_hint` h ON t.id = h.town_id WHERE t.id = 1 GROUP BY t.id
and it produces this result:
count(q.id)
count(h.id)
2
2
Do I need to use two statements? Or is it possible to query it in a single SQL statement? I am using MariaDB.
You can use union all and aggregation:
select town_id, sum(is_hint), sum(is_quiz)
from ((select town_id, 1 as is_hint, 0 as is_quiz
from hints
) union all
(select town_id, 0, 1
from quizzes
)
) t
group by town_id;
Alternatively, you can use correlated subqueries:
select t.*,
(select count(*) from hints h where h.town_id = t.id),
(select count(*) from quizzes q where q.town_id = t.id)
from towns t;
Two things to look out for:
JOINs are likely to multiply rows and throw off the counts.
Getting 0 values if a town has no hints or quizzes.
You can use COUNT (DISTINCT) if both the hint id and the quiz id are unique.
SELECT
count(distinct q.id),count(distinct h.id)
FROM `db_town` t
LEFT JOIN `db_quiz` q ON t.id = q.town_id
LEFT JOIN `db_hint` h ON t.id = h.town_id
WHERE t.id = 1 GROUP BY t.id

SQL statement to select all dead people

I have tables:
City:
zip, name,...
People:
id, city_zip(refers to city.zip), born_time, dead_time
I need to select data about cities where ALL people from that city are dead: born_time NOT NULL AND dead_time < NOW()
because we do not assume that someone is dead if we do not have information.
You can use not exists:
select c.*
from city c
where not exists (
select 1
from people p1
where p1.city_zip = c.zip and (dead_time is null or dead_time > now())
)
This would also return cities that have no people at all. If that's something you want to avoid, then another option is aggregation:
select c.*
from city c
inner join people p on p.city_zip = c.zip
group by c.zip
having max(case when dead_time is null or dead_time > now() then 1 else 0 end) = 0
select c.* ... from city c ... group by c.zip is valid standard SQL (assuming that zip is the primary key of table city). However, all databases do not support it, in which case you will need to enumerate the columns you want in both the select and group by clauses.

How do I display columns from two common table expression?

I have problems displaying columns from two common table expression. I created the first table by querying the student names and their mid-term grades and the other table the student names and their final-term grades.
CREATE TABLE MidTerm AS (SELECT Name, Score
FROM GRADE
WHERE TYPE = ''MidTerm
)
CREATE TABLE FinalTerm AS (SELECT Name, Score
FROM GRADE
WHERE TYPE = 'Final'
)
Both of the created have the same number of columns and the same variables. Now I want to display the Name, Score "MidTerm" and Score "FinalTerm", how can I achieve this? I manage to use UNION at the expense of SELECT * only. If I specify
Midterm table:
Name : Score
A : 50
B : 60
Finalterm table:
Name : Score
A : 70
B : 80
I want to join the CTE tables by displaying
Final Intended Result:
Name : Score "MidTerm" : Score "FinalTerm"
A : 50 : 70
B : 60 : 80
it would say invalid column identifier. How do I solve this?
A simple join will handle this:
SELECT m.NAME AS "Name",
m.SCORE AS "Score MidTerm",
f.SCORE AS "Score FinalTerm"
FROM MIDTERM m
LEFT OUTER JOIN FINALTERM f
ON f.NAME = m.NAME
db<>fiddle here
If you have two tables for midterm and final score as per comment in gordon's answer then just do join and you will get your result like this:
Select m.name,
M.score as midterm_score,
F.score as final_score
From midterm_table m
Join final_table f
on (m.name = f.name);
Cheers!!
I think that you are just looking for conditional aggregation:
select
name,
max(case when score = 'MidTerm' then score end) MidTerm,
max(case when score = 'Final' then score end) Final
from grade
where score in ('MidTerm', 'Final')
group by name
I am baffled. Use conditional aggregation:
SELECT Name,
MAX(CASE WHEN Type = 'MidTerm' THEN Score END) as midterm_score,
MAX(CASE WHEN Type = 'Final' THEN Score END) as final_score,
FROM GRADE
GROUP BY Name;
CTEs do not help with this query at all.
You could also do this using a JOIN:
select m.name, m.score as midterm_score, f.score as final_score
from grade m join
grade f
on m.name = f.name and
m.type = 'midterm' and
f.type = 'final';
Note that this only shows names with both scores.
Add student's id in those tables and use it to join them and gather the columns that you need.
I dont believe that create this two tables is realy a good idea,
but, ok, I don't know the complexity of your calculations to get the score.
anyway, I would suggest to you consider the creation of an view for that instead of create those table.

How do I get counts for different values of the same column with a single totals row, using Postgres SQL?

So I have a list of children and I want to create a list of how many boys and girls there are in each school and a final total count of how many there are.
My query including logic
select sch.id as ID, sch.address as Address, count(p.sex for male) as boycount, count(p.sex for female) as girlcount
from student s
join school sch on sch.studentid = s.id
join person p on p.studentid = s.id
Obviously I know this query wont work but I dont know what to do further from here. I thought about nested query but im having difficulty getting it to work.
I found a similar question for postgres 9.4
Postgres nested SQL query to count field. However I have Postgres 9.3.
Final result would be like :
WARNING
Depending on the data type of the school ID, you may get an error with this union. Consider casting the school ID as a varchar if it is of type INT.
SELECT
sch.id as ID, /*Consider casting this as a varchar if it conflicts with
the 'Total' text being unioned in the next query*/
sch.address as Address,
SUM(CASE
WHEN p.sex = 'male'
THEN 1
ELSE 0
END) AS BoyCount,
SUM(CASE
WHEN p.sex = 'female'
THEN 1
ELSE 0
END) AS GirlCount
FROM
student s
JOIN school sch
ON sch.studentid = s.id
JOIN person p
ON p.studentid = s.id
UNION ALL
SELECT
'Total' as ID,
NULL as Address,
SUM(CASE
WHEN p.sex = 'male'
THEN 1
ELSE 0
END) AS BoyCount,
SUM(CASE
WHEN p.sex = 'female'
THEN 1
ELSE 0
END) AS GirlCount
FROM
person p