SQL WHERE both values of a variable exist - sql

Suppose that I have a table of global box office information including columns "filmName", "country" and "earnings". The question is how to find out the films that sell better in country A than in country B. Here is my answer:
SELECT filmName
FROM Boxoffice
WHERE (SELECT earnings FROM Boxoffice WHERE country = "A") >
(SELECT earnings FROM Boxoffice WHERE country = "B")
GROUP BY filmName
But then I found out that there are some films that are not shown in both countries. I wonder how I can add the condition to the films that are shown in both countries to my existed answer. And I also have no idea if my answer has any problem since I do not have the real data.

I think a self join would be simpler:
SELECT a.filmname
FROM boxoffice a
JOIN boxoffice b ON a.country = 'A' AND b.country = 'B' AND a.earnings > b.earnings;

It seems filmname and country is the unique key for your table, i.e. there is one row per film and country.
One way to get films that sell better in country A than B is to aggregate and compare the earnings in the HAVING clause:
select filmname
from boxoffice
group by filmname
having max(case when country = 'A' then earnings end) >
max(case when country = 'B' then earnings end)
order by filmname;
Another way is to join, e.g.:
select a.filmname
from (select * from boxoffice where country = 'A') a
join (select * from boxoffice where country = 'B') b
on a.filmname = b.filmname and a.earnings > b.earnings
order by a.filmname;

Related

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 to create a query to join three tables and make calculations in SQL?

I'm just at the beginning of my SQL studies and can't figure out how to resolve the next problem.
So, there are three tables:
! given tables
The task is: "Get number of pet type per owner"
Write a query to generate the result below:
! desired output
The best result I have for the moment:
SELECT owners.OWNER_NAME, COUNT(pets.OWNER_ID) AS pets
FROM owners
JOIN pets ON owners.ID = pets.OWNER_ID
JOIN pet_type ON pets.TYPE = pet_type.ID
GROUP BY owners.OWNER_NAME;
It returns first column with owner names and second column with the sum of particular owner pets.
Will appreciate any help.
You need conditional aggregation:
SELECT
o.OWNER_NAME,
SUM(CASE WHEN t.name = 'CAT' THEN 1 ELSE 0 END) CAT,
SUM(CASE WHEN t.name = 'DOG' THEN 1 ELSE 0 END) DOG,
SUM(CASE WHEN t.name = 'SNAKE' THEN 1 ELSE 0 END) SNAKE
FROM owners o
JOIN pets p ON o.ID = p.OWNER_ID
JOIN pet_type t ON p.TYPE = t.ID
GROUP BY o.OWNER_NAME;
I use name as the name of the column describing the type in table pet_type. Change it to the actual name of the column.
Check this. To Get number of pet type per owner, this is sufficient to join only Pets table with the owners table. A DISTINCT count of Pet.Type will give your desired output.
SELECT
owners.ID,
owners.OWNER_NAME,
COUNT(DISTINCT pets.TYPE) AS Num_Pet_Type
FROM owners
INNER JOIN pets ON owners.ID = pets.OWNER_ID
GROUP BY owners.ID,owners.OWNER_NAME;
If you wants number of Pet per type, use this below script-
SELECT
owners.ID,
owners.OWNER_NAME,
pets.TYPE,
COUNT(*) AS Num_Of_Pet
FROM owners
INNER JOIN pets ON owners.ID = pets.OWNER_ID
GROUP BY owners.ID,owners.OWNER_NAME,pets.TYPE;

SELECT * FROM table in addition of aggregation function

Short context:
I would like to show a list of all companies except if they are in the sector 'defense' or 'government' and their individual total spent on training classes. Only the companies that have this total amount above 1000 must be shown.
So I wrote the following query:
SELECT NAME, ADDRESS, ZIP_CODE, CITY, SUM(FEE-PROMOTION) AS "Total spent on training at REX"
FROM COMPANY INNER JOIN PERSON ON (COMPANY_NUMBER = EMPLOYER) INNER JOIN ENROLLMENT ON (PERSON_ID = STUDENT)
WHERE SECTOR_CODE NOT IN (SELECT CODE
FROM SECTOR
WHERE DESCRIPTION = 'Government' OR DESCRIPTION = 'Defense')
GROUP BY NAME, ADDRESS, ZIP_CODE, CITY
HAVING SUM(FEE-PROMOTION) > 1000
ORDER BY SUM(FEE-PROMOTION) DESC
Now what I actually need is, instead of defining every single column in the COMPANY table, I would like to show ALL columns of the COMPANY table using *.
SELECT * (all tables from COMPANY here), SUM(FEE-PROMOTION) AS "Total spent on training at REX"
FROM COMPANY INNER JOIN PERSON ON (COMPANY_NUMBER = EMPLOYER) INNER JOIN ENROLLMENT ON (PERSON_ID = STUDENT)
WHERE SECTOR_CODE NOT IN (SELECT CODE
FROM SECTOR
WHERE DESCRIPTION = 'Government' OR DESCRIPTION = 'Defense')
GROUP BY * (How to fix it here?)
HAVING SUM(FEE-PROMOTION) > 1000
ORDER BY SUM(FEE-PROMOTION) DESC
I could define every single column from COMPANY in the SELECT and that solution will do the job (as in the first example), but how can I make the query shorter using "SELECT * from the table COMPANY"?
The key idea is to summarize in the subquery to get the total spend for the company. This allows you to remove the aggregation from the outer query:
select c.*, pe.total_spend
from company c join
sector s
on c.sector_code = s.code left join
(select p.employer, sum(e.fee - e.promotion) as training_spend
from person p join
enrollment e
on p.person_id = e.student
group by p.employer
) pe
on pe.employer = c.company_number
where s.sector not in ('Government', 'Defense') and
pe.total_spend > 1000

List school name in more than 2 states based on student loan

tbl1: schID, SChName
tbl2: SchID, stuid, city, state, loan_purpose, Action
I have two tables, tbl1 lists school ids and school names, and tbl2 lists studentid, loan purpose and action taken by school.
I need help with writing a query to list school names that accepted
applications in more than 2 states (when loan type = "FS" and action is "A).
You'll want to use a GROUP BY using the WHERE to apply your row-level filters (action and loan purpose), and then HAVING for the aggregation filter (state count).
SELECT
tbl1.SchID,
tbl1.SchName
FROM tbl1
INNER JOIN tbl2
ON tbl1.SchID = tbl2.SchID
WHERE tbl2.loan_purpose = 'FS'
AND tbl2.Action = 'A'
GROUP BY
tbl1.SchID,
tbl1.SchName
HAVING COUNT(DISTINCT tbl2.state) > 2
ORDER BY
tbl1.SchName
select SCHName FROM(
select tbl1.schname, count(*) FROM
tbl1 INNER JOIN tbl2 on tbl1.SCHID = tbl2.SCHID
WHERE tbl2.loan_purpose = 'FS' AND tbl2.ACTION = 'A'
GROUP BY SCHName
HAVING count(*) > 2
)