Achieving an SQL Join - sql

I have three tables: Book which holds id, title etc..
member which holds id, name etc...
and loan which holds member_id, book_id, returnDate etc..
I am trying to write a query which gets me back all member details if they have loaned 3 or under three books. So far i have:
SELECT * FROM member m
JOIN member_loans_book mlb ON m.id = mlb.Member_id
HAVING count(mlb.Book_id) <= 3
and it compiles but returns nothing, am i far wrong?
any pointers appreciated.

See sqlFiddle
SELECT m.MemberId, m.Name, COUNT(*)
FROM Member m
JOIN Loan l ON m.MemberId = l.MemberId
GROUP BY m.MemberId, m.Name
HAVING COUNT(*) <= 3

Related

How do I put two conditions in having in SQL Server?

I have to get members that are on five different groups and those groups should have 5 members each
Member (idMember, nameMember)
Group (idGroup)
Belongs (idMember, idGroup)
Groups have members, members are in groups, a member can be in as many groups as he wants but a member can't be twice on the same group
I made something like
select idMember, nameMember
from Member m, Group g
where idMember in (select b.idMember
from Belongs b)
group by idMember, nameMember
having (select count(*)
from Belongs b
where b.idMember = m.idMember)>5
and
(select count (*)
from Belongs b
where b.idGroup = g.idGroup /*??*/)>5
and there I don't know how to relate belongs with group
Approach this type of problem in steps. The following gets the members that are in five groups:
select b.idMember
from belongs b -- Note: `group` is a reserved word so a bad name for a table
group by b.idMember
having count(*) = 5;
The following gets the groups that have five members:
select b.idGroup
from belongs b
group by b.idGroup
having count(*) = 5;
(Interesting symmetry.)
If you want to limit the first query to the groups in the second, then a simple way is to use in:
select b.idMember
from belongs b
where b.groupId in (select g.idGroup
from belongs b
group by b.idGroup
having count(*) = 5
)
group by b.idMember
having count(*) = 5;
When you are dealing with complex queries, build them up one step at a time.
Note: group is a really bad name for a table because it is a reserved word. And, if you want the table name then use JOIN to join to the members table to get the right name.
EDIT:
You can use join to get columns from member:
select b.idMember, m.name
from belongs b join
member m
on b.idMember = m.idMember
where b.groupId in (select g.idGroup
from belongs b
group by b.idGroup
having count(*) = 5
)
group by b.idMember, m.name
having count(*) = 5;

Oracle sql - referencing tables

My school task was to get names from my movie database actors which play in movies with highest ratings
I made it this way and it works :
select name,surname
from actor
where ACTORID in(
select actorid
from actor_movie
where MOVIEID in (
select movieid
from movie
where RATINGID in (
select ratingid
from rating
where PERCENT_CSFD = (
select max(percent_csfd)
from rating
)
)
)
);
the output is :
Gary Oldman
Sigourney Weaver
...but I'd like to also add to this select mentioned movie and its rating. It accessible in inner selects but I don't know how to join it with outer select in which i can work just with rows found in Actor Table.
Thank you for your answers.
You just need to join the tables properly. Afterwards you can simply add the columns you´d like to select. The final select could be looking like this.
select ac.name, ac.surname, -- go on selecting from the different tables
from actor ac
inner join actor_movie amo
on amo.actorid = ac.actorid
inner join movie mo
on amo.movieid = mo.movieid
inner join rating ra
on ra.ratingid = mo.ratingid
where ra.PERCENT_CSFD =
(select max(percent_csfd)
from rating)
A way to get your result with a slightly different method could be something like:
select *
from
(
select name, surname, percent_csfd, row_number() over ( order by percent_csfd desc) as rank
from actor
inner join actor_movie
using (actorId)
inner join movie
using (movieId)
inner join rating
using(ratingId)
(
where rank = 1
This uses row_number to evaluate the "rank" of the movie(s) and then filter for the movie(s) with the highest rating.

Query with aggregating data from 2 tables

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;

Better way to demand, in SQL, that a column contains every specified value

Imagine you have two tables, with a one to many relationship.
For this example, I will suggest that there are two tables: Person, and Homes.
The person table holds a persons name, and gives them an ID. The homes table, holds the association of homes to a person. PID joins to "Person.ID"
And, in this tiny DB, a person can have no homes, or many homes.
I hope I drew that right.
How do I write a select, that returns everyone with every specified house type?
Let's say these are valid "Types" in the homes table:
Cottage, Main, Mansion, Spaceport.
I want to return everyone, in the Person table, who has a spaceport and a Cottage.
The best I could come up with was this:
SELECT DISTINCT( p.name ) AS name
FROM person p
INNER JOIN homes h ON h.pid = p.id
WHERE 'spaceport' in (
SELECT DISTINCT( type ) AS type
FROM homes
WHERE pid = p.id
)
AND 'cottage' in (
SELECT DISTINCT( type ) AS type
FROM homes
WHERE pid = p.id
)
When I wrote that, it works, but I'm pretty sure there has to be a better way.
The HAVING clause here will guarantee that the persons returned have both types, not just one or the other.
SELECT p.name
FROM person p
INNER JOIN homes h
ON p.id = h.pid
AND h.type IN ('spaceport', 'cottage')
GROUP BY p.name
HAVING COUNT(DISTINCT h.type) = 2
select * from homes;
home_id person_id type
--
1 1 cottage
2 1 mansion
3 2 cottage
4 3 mansion
5 4 cottage
6 4 cottage
To find the id numbers of every person who has both a cottage and a mansion, group by the id number, restrict the output to cottages and mansions, and count the distinct types.
select person_id
from homes
where type in ('cottage','mansion')
group by person_id
having count(distinct type) = 2;
person_id
--
1
You can use this query in a join to get all the columns from the person table.
select person.*
from person
inner join (select person_id
from homes
where type in ('cottage','mansion')
group by person_id
having count(distinct type) = 2) T
on person.person_id = T.person_id;
Thanks to Joe for pointing out an error in my count().
Not sure about the performance on this one, but here goes:
SELECT PID FROM (
SELECT PID, COUNT(PID) cnt FROM (
SELECT DISTINCT PID, Type FROM Homes
WHERE Type IN ('Type1', 'Type2', 'Type3')
) a
GROUP BY PID
) b
WHERE b.cnt = 3
You'd have to dynamically generate your IN clause as well as the WHERE b.CNT clause.

SQL query for finding row with same column values that was created most recently

If I have three columns in my MySQL table people, say id, name, created where name is a string and created is a timestamp.. what's the appropriate query for a scenario where I have 10 rows and each row has a record with a name. The names could have a unique id, but a similar name none the less. So you can have three Bob's, two Mary's, one Jack and 4 Phil's.
There is also a hobbies table with the columns id, hobby, person_id.
Basically I want a query that will do the following:
Return all of the people with zero hobbies, but only check by the latest distinct person created, if that makes sense. Meaning if there is a Bob person that was created yesterday, and one created today.. I only want to know if the Bob created today has zero hobbies. The one from yesterday is no longer relevant.
select pp.id
from people pp, (select name, max(created) from people group by name) p
where pp.name = p.name
and pp.created = p.created
and id not in ( select person_id from hobbies )
SELECT latest_person.* FROM (
SELECT p1.* FROM people p1
WHERE NOT EXISTS (
SELECT * FROM people p2
WHERE p1.name = p2.name AND p1.created < p2.created
)
) AS latest_person
LEFT OUTER JOIN hobbies h ON h.person_id = latest_person.id
WHERE h.id IS NULL;
Try This:
Select *
From people p
Where timeStamp =
(Select Max(timestamp)
From people
Where name = p.Name
And not exists
(Select * From hobbies
Where person_id = p.id))