Query with aggregating data from 2 tables - sql

I have three tables in my Oracle db:
Peoples:
IdPerson PK
Name
Surname
Earnings:
IdEarning
IdPerson
EarningValue
Awards:
IdAward PK
IdPerson FK
AwardDescription
A person can have many earnings.
An earning can have many or no any earnings.
A person can have many awards, one award, or no any award.
I want to make a query that will return 3 columns:
Surname
SUM of all EarningValue of person with this surname
COUNT of all Awards for this person
An important thing is that i also need to display: 0 value if person don't have any award or earning. There is a possibility that person have an award but don't have any earning.
Is it possible to make such query?

SELECT p.IdPerson,
p.Surname,
NVL(SUM(e.EarningValue), 0) as SumEarnings,
COUNT(a.IdAward) as CntAwards
FROM Peoples p
LEFT JOIN Earnings e ON p.IdPerson = e.IdPerson
LEFT JOIN Awards a ON p.IdPerson = a.IdPerson
GROUP BY p.IdPerson,
p.Surname;
What returns this:
SELECT p.Surname,
(SELECT NVL(SUM(e.EarningValue), 0)
FROM Earnings e WHERE e.IdPerson = p.IdPerson) as SumEarnings,
(SELECT COUNT(aIdAward)
FROM Awards a WHERE a.IdPerson = p.IdPerson) as CntAwards
FROM Peoples p

yes of-course it is possible.
you just need to join the tables using multiple join queries and then apply sum() function to get sum of earnings and Count() to count the no. of awards.
Try the below query:
SELECT P.Surname,
Sum(E.EarningValue)AS Total_Earnings,
Count(A.IdAward) AS total_awards
FROM Peoples P
LEFT JOIN Earnings E
ON P.IdPerson = E.IdPerson
LEFT JOIN Awards A
ON A.IdPerson = P.IdPerson
GROUP BY P.IdPerson;

Yes it is possible, you just have to join the tables
SELECT Peoples.Surname, SUM(Earnings.EarningValue) as Earnings, COUNT(Awards. IdPerson) as Awards
FROM Peoples
INNER JOIN Earnings
ON Peoples.IdPerson = Earnings.IdPerson
INNER JOIN Awards
ON Peoples.IdPerson = Awards.IdPerson
GROUP BY Peoples.IdPerson;

Related

How to use count() without the output changing when I use join -Postgresql

I need to find the sum of all the rental days of a car dealership (including
period_begin and period_end, not unique) for all cars of that department.Divided by the total number of different (unique) employees that department ever had.
I have 5 tables
Department(PK departmentnr, name, FK postcode, FK place_name)
Employee(PK employeenr, FK email)
Contract(PK periode_begin, PK periode_end, FK departmentnr, FK employeenr)
registerform (,PK periode_end,PK,periode_end,FKemail,FK,
Fk numberplate,periode_end,periode_begin)
car(PK numberplate,FK departmentnr,FK Brand,FK model)
when I go step by step
part 1
The total employees per department
select departmentnr,Count(employeenr)FROM contract
group by departmentnr
part 2
the amount of days the cars were hired
SELECT DISTINCT departmentnr,
Sum((( Date(periode_end) - Date(periode_begin) + 1 ))) AS
average
FROM registerform r
INNER JOIN car w using(numberplate)
GROUP BY departmentnr
I get the correct ouput but when I try to get these 2 together
SELECT distinct departmentnr,
(
sum(((date(r.periode_end) - date(r.periode_begin) + 1))) / (
select
count(employeenr))
)
as average
from
registerform r
inner join
car w using(numberplate)
inner join
contract using(departmentnr)
inner join
employee using(employeenr)
group by
departmentnr
then my output gets absurd.
How can I fix this and is there a way to make the code more efficient.
Aggregated before you JOIN. So, one method is:
SELECT c.departmentnr, co.num_employees,
Sum( Date(r.periode_end) - Date(r.periode_begin) + 1 ) AS average
FROM registerform r JOIN
car c
USING (numberplate) LEFT JOIN
(SELECT co.departmentnr, Count(*) as num_employees
FROM contract co
GROUP BY co.departmentnr
) co
ON co.departmentnr = c.departmentnr
GROUP BY c.departmentnr, co.num_employees;

SQL ORDER BY number of rows?

How do you ORDER BY number of rows found in another table? I have a table for animals (these are livestock animals) and another table for awards. When an animal wins an award, the award gets added to the awards table.
People want to be able to find the animals who have won the most awards (WHERE award type is 1), ordered from most awards to least. How do I ORDER BY how many awards they have if the awards are in a separate table each with their own row?
SELECT animals.id
FROM animals
LEFT JOIN awards ON animals.id = awards.animalid
WHERE awards.type = 1
ORDER BY...
You would seem to want GROUP BY:
SELECT a.id
FROM animals a LEFT JOIN
awards aw
ON a.id = aw.animalid AND aw.type = 1
GROUP BY a.id
ORDER BY COUNT(aw.animalid) DESC;

joining a table on 2 fields

I want to pull a person and their supervisor names from a table. The persons table has the supervisor_id and the person_id. The names table has name_id and a Full Name field. If I join Person ON either supervisor_id or person_id, how do I get the other to display as well?
You need to join twice, one for each relationship you have:
SELECT
-- Persons' columns
P.*,
-- Superviser name columns
SN.*,
-- Person name columns
PN.*
FROM
persons AS P
LEFT JOIN names AS SN ON P.supervisor_id = SN.name_id
LEFT JOIN names AS PN ON P.person_id = PN.name_id
Or you can join with an OR clause, but you won't be able to know which record did you join with unless you check with a CASE.
SELECT
-- Persons' columns
P.*,
-- name columns
N.*,
IsSupervisor = CASE WHEN P.supervisor_id = N.name_id THEN 'Yes' ELSE 'No' END
FROM
persons AS P
LEFT JOIN names AS N ON
P.supervisor_id = N.name_id OR
P.person_id = N.name_id
This last approach will display 2 rows as it will match either one or the other on different occasions, not both with the same persons row (as the first example).
A (self)join is what you need:
select p.*, supervisor=ps.name
from Person p join person ps on p.supervisor_id=ps.id

SQL query return name and count of specific attributes across multiple tables

Given multiple tables I'm trying to write a query that returns the names that satisfies a specific count clause.
I have the tables:
genre(genre, movieid)
moviedirectors(movieid, directorid)
directors(directorid, firstname, lastname)
I want to write a query that returns the first and last name of directors that directed at least 50 movies of the genre comedy, and return that number as well.
This is what I have
select d.fname, d.lname, count(*)
from genre g, directors d, moviedirectors md
where g.genre='Comedy' and g.movieid=md.movieid and
md.directorid=d.directorid
group by d.id
having count(*) >= 50
I believe this should be correct but when I run this query on the command line it never finishes. I waited 30 minutes and got no results.
you need inner joins:
SELECT d.fname
d.lname
FROM genre g
INNER JOIN moviedirectors md
ON g.movieid = md.movieid
INNER JOIN directors d
ON md.directorid = d.directorid
WHERE g.genre = 'Comedy'
GROUP BY d.fname, -- group by columns in select
d.lname
HAVING COUNT(*) >= 50
select c.firstname,c.lastname,count(e.movieid) from (select a.* from directors a,movie b where b.genre = 'Comedy' and b.movieid=a.movieid)d,directors c where c.directorid=d.directorid group by e.movieid having count(e.movieid)>50;

Query to select related data from two tables in which one table has no related fields in third table

I have three tables in my Oracle db:
Peoples:
IdPerson PK
Name
Surname
Earnings:
IdEarning PK
IdPerson
EarningValue
Awards:
IdAward PK
IdPerson FK
AwardDescription
So one person can have many earnings, one earning or can have no any earnings. Also one person can have many awards, one award, or no any award.
I have to select Surname and AwardDescription but only for people who have any earnings, because it is possible to have some award but, also don't have any earning!
My problem is to make a correct group by statement. I use query posted below and I am selecting surname of person with a description of award, but it is duplicating for each row in Earnings for this person.
SELECT AwardDescription, Surname
FROM Awards
INNER JOIN People ON People.IdPerson= Awards.IdPerson
INNER JOIN Earnings ON Earnings .IdPerson= People.IdPerson;
How to group it and avoid duplicating rows for each earning of person?
One person can be in many rows, but with different awards.
You could add DISTINCT to your query:
SELECT DISTINCT AwardDescription, Surname
FROM Awards
INNER JOIN People ON People.IdPerson= Awards.IdPerson
INNER JOIN Earnings ON Earnings .IdPerson= People.IdPerson;
Or another option is to use EXISTS:
SELECT AwardDescription, Surname
FROM Awards
INNER JOIN People P ON P.IdPerson= Awards.IdPerson
WHERE EXISTS (
SELECT 1
FROM Earnings E
WHERE P.IdPerson = E.IdPerson);
Do a left outer join among the tables like
SELECT p.Surname, a.AwardDescription, e.EarningValue
FROM People p
LEFT JOIN Awards a ON p.IdPerson= a.IdPerson
LEFT JOIN Earnings e ON e.IdPerson= p.IdPerson
WHERE a.AwardDescription IS NOT NULL
OR e.EarningValue IS NOT NULL;