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

Related

SQL WHERE both values of a variable exist

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;

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

SQL JOIN on one field or the other

Trying to order a family by father's name or, if there is no father, then the mother's name where the names are in a separate "person" table, something like:
SELECT DISTINCT family.myid FROM family
JOIN person
ON family.father_id = person.myid OR
family.mother_id = person.myid
ORDER BY person.surname,
person.given_name;
In this version, the families without fathers end up unsorted at the bottom. Would like families without fathers to appear in the order by the mother's name. Sqlite SQL will suffice.
Basically, you need a separate join for the fathers and the mothers:
select f.*
from family f left join
person d
on f.father_id = d.myid left join
person m
on f.mother_id = m.myid
order by (case when d.myid is null then m.surname else d.surname end),
(case when d.myid is null then m.given_name else d.given_name end);
Because a value could be missing, this should be a left join.
COALESCE should work
ORDER BY COALESCE(NULLIF(b.surname, ''), c.surname),
COALESCE(NULLIF(b.given_name, ''), c.given_name)

SQL join two tables with a link table between - GORM/Grails

I am trying to build an output as such:
Name source source source
Tim Other TV Radio
Where I want to combine a Person table and a Source table:
Person
ID Name
1 Tim
Source
ID Name
1 Other
2 TV
3 Radio
Person_Source
p_id s_id
1 1
1 2
I have a query that builds out each source for time and outputs in multiple lines. I'm looking for a single result in the result set
select source.name
from person left join person_source
on person_source.person_source_id = person.id join source
on source.id = person_source.source_id
You could add a DISTINCT to your query:
select DISTINCT source.name
from person left join person_source
on person_source.person_source_id = person.id join source
on source.id = person_source.source_id
Not sure what flavour of SQL this is supposed to be but you could try the following:
SELECT
p.Name,
MAX(CASE s.ID WHEN 1 THEN s.Name END) AS source1,
MAX(CASE s.ID WHEN 2 THEN s.Name END) AS source2,
MAX(CASE s.ID WHEN 3 THEN s.Name END) AS source3
FROM Person p
INNER JOIN Person_Source ps ON p.ID = ps.p_id
INNER JOIN Source s ON ps.s_id = s.ID
GROUP BY
p.ID,
p.Name
You could also check the sources differently, using their names, like this:
…
MAX(CASE s.Name WHEN 'TV' THEN s.Name END) AS source1,
MAX(CASE s.Name WHEN 'Radio' THEN s.Name END) AS source2,
MAX(CASE s.Name WHEN 'Other' THEN s.Name END) AS source3
…
Note that a SQL query always return a fixed number of columns. You'll need to decide beforehand how many sources should be returned by the query, and if the number should depend on the actual number of possible sources in the Source table, you'll have to build your query dynamically, to include all the necessary sources but no more than necessary.