JOIN mutiple tables in SQL SERVER - sql

I have like above Logical ERD. I'm alright with it BUT I can't understand how to display the correct information.
For example:
Need to lists groups and the members belonging to each group. For each group show the ID and its name. For each member, show the unique identifier, the name, gender, date of birth and identifier of their group leader.
Ok, we have group table and group member table.
SELECT group ID, group name
FROM group;
SELECT member ID, name, gender, D.O.B, Leader ID
From group member;
I understand that this is wrong, I just not understand how to display right information, I can imagine it but can't write it down O_o....stuck a bit
One more question, how about the supervisor, I understand that it goes through (Activity Participant) BUT how do i have to create the activity table with this supervisor as foreign key?

This is what you can do:
SELECT
P.Name,
P.DOB,
P.Gender,
G.GroupName,
GL.PersonId
FROM Person P
INNER JOIN GroupMember GM ON GM.PersonId = P.PersonId
INNER JOIN Group G ON G.GroupId = P.GroupId
INNER JOIN GroupLeader GL ON GL.GroupId = G.GroupId
You can JOIN more tables and build your query as shown above.

Related

How can I filter a table for maximum AND minimum values - and then join that table to another one?

I know this is probably a very newbie question but I'm very new to SQL so please, please bare with me.
Bascially, my 'Person' table contains information like first and last name, address, date of birth, and my 'Member' table contains information specific to members (i.e. monthly renewal fee, bookings made, etc.)
MembersID is a foreign key which references Person.ID. Essentially, what I'm trying to do is filter out my Member table so that only the members who have made the least AND most bookings are displayed, then, I want to join this to the person table so that their corresponding information (i.e. their names, dates of birth) are also displayed.
I have no problem filtering out the Member table to show only the members who have the most and least bookings made - and I can also join the two tables together so that ALL members are shown.
But I do not understand how to filter the member table - and then join ONLY those filtered results to the person table... I feel like I have both pieces to the puzzle I just can't put it together.
This is what I mean:
select
p.FirstName,
p.LastName,
p.DateOfBirth,
p.[Address],
m.BookingsMade
from Person p
inner join Member m on p.ID = m.MemberID;
That's me joining the two tables so that all information is displayed, below is me filtering out the member table (seperately) so that only the highest and lowest bookings are displayed:
select
m.MemberID,
m.[MonthlyFee ($)],
m.BookingsMade
from Member m
where m.BookingsMade = (select min(BookingsMade) from Member)
or m.BookingsMade = (select max(BookingsMade) from Member)
order by BookingsMade ASC;
How can I put this together?
You can add pre-join conditions to your ON clause, so try this:
select
p.FirstName,
p.LastName,
p.DateOfBirth,
p.[Address],
m.BookingsMade
from Person p
inner join Member m on p.ID = m.MemberID
AND (m.BookingsMade = (select min(BookingsMade) from Member)
OR or m.BookingsMade = (select max(BookingsMade) from Member))
order by BookingsMade ASC;

Join with count

I need to write SQL query like:
Show all countries with more than 1000 users, sorted by user count.
The country with the most users should be at the top.
I have tables:
● Table users (id, email, citizenship_country_id)
● Table countries (id, name, iso)
Users with columns: id, email, citizenship_country_id
Countries with columns: id, name, iso
SELECT countries.name,
Count(users.citiizenship_country_id) AS W1
FROM countries
LEFT JOIN users ON countries.id = users.citizenship_country_id
GROUP BY users.citiizenship_country_id, countries.name
HAVING ((([users].[citiizenship_country_id])>2));
But this does not work - I get an empty result set.
Could you please tell me what I'm doing wrong?
A LEFT JOIN is superfluous for this purpose. To have 1000 users, you need at least one match:
SELECT c.name, Count(*) AS W1
FROM countries c JOIN
users u
ON c.id = u.citizenship_country_id
GROUP BY c.name
HAVING COUNT(*) > 1000;
Notice that table aliases also make the query easier to write and to read.
Group by country name and use HAVING Count(u.citiizenship_country_id)>1000, it filters rows after aggregation:
SELECT c.name,
Count(u.citiizenship_country_id) AS W1
FROM countries c
INNER JOIN users u ON c.id = u.citizenship_country_id
GROUP BY c.name
HAVING Count(u.citiizenship_country_id)>1000
ORDER BY W1 desc --Order top counts first
;
As #GordonLinoff pointed, you can use INNER JOIN instead of LEFT JOIN, because anyway this query does not return counries without users and INNER JOIN performs better because no need to pass not joined records to the aggregation.

Writing sql without aggregation function

I want to write a SQL query for the problem as defined below, I am not sure about the answer, can anyone help me? Is the answer correct, or if not, how can I improve it?
I have used aggregation function, how can I improve it, to write an sql without aggregation function?
Let us consider the following relational schema about physicians and departments:
PHYSICIAN (PhysicianId, Name, Surname, Specialization, Gender, BirthDate, Department);
Let every physician be univocally identified by a code and characterized by a name, a surname, a specialization (we assume to record exactly one specialization for each physician), a gender, a birth date, and the relative department (each physician is assigned to one and only one department).
DEPARTMENT (Name, Building, Floor, Chief)
Let every department be univocally identified by a name and characterized by its location (building and floor) and a chief.
Let us assume that a physician can be the chief of at most one department (the department he/she belongs to). We do not exclude the possibility for two distinct departments to be located at the same floor of the same building.
I want to formulate an SQL query to compute the following data (exploiting aggregate functions only if they are strictly necessary):
the departments which have no male physicians and with at least two physicians whose home city is Venice.
My answer is as below:
select d.name
from department d
where d.name in (select p.department from physician p where p.gender =! 'Male')
and d.name in (select p.department from physician p
where HomeCity = 'Venice'
group by p.PhysicianId
having count > 2)
Or:
select d.*
from department d
inner join physician p on d.name=p.department and
p.gender=!"Male"
left join physician o where d.name=o.department and o.birthdate='venice'
groupby birthdate
having sum(o. physicianID) >2
Assuming that both the conditions should apply at the same time, I'd say your answer is correct. I would modify it just a bit:
select d.name
from department d
where not exists (select 1 from physician p where p.gender = 'Male' and p.Department = d.name)
and d.name in (select p.department from physician p
where HomeCity = 'Venice'
group by p.department, p.HomeCity
having count >= 2)
If you go with the inner join solution, you will have to apply a distinct to your select, which will make your query slower.
The not exists clause I propose, will create an execution plan, similar to inner join. That is you will not need to compare each name with all the names of the departments without Male employees.
You can't avoid the aggregate function if you want to count something. There is a workaround with Over in TSQL but I would not recommend it.
Despite conditions are different the main idea is the same as in improving the written sql query.
You need to use the same approach but substitute your own join and where conditions.
Update
Here is a version for your case:
select distinct d.*
from departments d
left join physicians m on d.name = m.department and m.gender = 'Male'
inner join physicians v1 on d.name = v1.department and v1.homecity = 'Venice'
inner join physicians v2 on d.name = v2.department and v2.homecity = 'Venice' and v2.id <> v1.id
where m.id is null

SQL select multiple rows and order them by id

I got a Student with a few parameters, like Id, Name, etc.
Now I got another one table called ACCOUNTS, they have an id and a name...
the relation between these two is OneToMany (one student can have more accounts)
and i need a SQL query to show all accounts for the student...here is what i have, but it's not working...
"Select distinct s from Student s left join fetch s.accounts where s.id=:studId"
I should say that the Student has a field called accounts
And at the end of the query, i placed already ORDER BY and then the following code didn't work:
s.accounts.id
account.id
student.account.id
So...long story short...the query above, shows the accounts of the specific student...now i just need to order them by the id of the accounts
Any suggestions?
As OP confirmed adding it as answer.
select distinct s.id, s.name, a.id
from students s left outer join accounts a on s.id = a.id
order by s.id, a.id;
one student can have more accounts
So it's two tables:
student (id, name, ...)
accounts (id, student_id, ...)
I should say that the Student has a field called accounts
That makes no sense. By this you would store one account per student and several students could share one account. So I stick to the first statement that one student can have several accounts and the tables look more or less like I've shown above.
You want to see accounts for one student. So you select from accounts where the student ID matches:
select * from accounts where student_id = :studId order by account.id;
If you want to show student data along, you'd join the tables instead:
select *
from student s
left join accounts a on a.student_id = s.id
where s.id = :studId
order by a.id;

SQL Access: how to obtain output involving multiple tables without running 2 queries?

I would like to find out the most popular genre of film for a certain age group, for example 20-30 year-olds. I'm quite new to SQL and would appreciate any help I can get, apologies if this is too minor.
The relevant tables for this query are:
FILM {FID (PK), ..., Film_Title}
MEMBER {MID (PK), ..., Date_of_Birth}
LIST {MID (FK), FID (FK)}
GENRE {GID (PK), Genre}
FILM_ACTOR_DIRECTOR_GENRE {FID (FK), ..., GID (FK)}
FILM and MEMBER table should be quite self-explanatory, while a LIST is a selection of films a MEMBER wishes to rent. It's like a shopping basket. Each member only has one list and each list can contain many films. FILM_ACTOR_DIRECTOR_GENRE contains Genre belonging to each film. Each film can only have one genre.
So far I have managed to get an output which shows:
Genre # People Aged 20-30
------- -------------------
Action 5
Comedy 4
Horror 2
etc. etc.
However it involves creating a table and then running another query. Is there a way to obtain the most popular genre within a particular age group without having to run 2 separate queries?
The 2 queries I've used are:
SELECT DISTINCT Genre.Genre_Name, Member.Date_of_Birth
INTO Genre_by_Age
FROM
((((Genre
INNER JOIN Film_Actor_Director_Genre ON Genre.GID = Film_Actor_Director_Genre.GID)
INNER JOIN Film ON Film_Actor_Director_Genre.FID = Film.FID)
INNER JOIN List ON Film.FID = List.FID)
INNER JOIN Member ON Member.MID = List.MID)
WHERE (((Member.[Date_of_Birth]) Between #4/16/1995# And #4/16/1985#));
for creating the new table with information I want, and:
SELECT Genre_Name, COUNT(*) as Number_of_People_aged_20_to_30
FROM Genre_by_Age
GROUP BY Genre_Name
ORDER BY COUNT(*) DESC;
to obtain the output shown above.
Is there a way to obtain the above result without running 2 separate queries? Thanks for your time!
How about using a subquery?
SELECT Genre_Name, COUNT(*) as Number_of_People_aged_20_to_30
FROM (SELECT DISTINCT Genre.Genre_Name, Member.Date_of_Birth
FROM ((((Genre
INNER JOIN Film_Actor_Director_Genre ON Genre.GID = Film_Actor_Director_Genre.GID)
INNER JOIN Film ON Film_Actor_Director_Genre.FID = Film.FID)
INNER JOIN List ON Film.FID = List.FID)
INNER JOIN Member ON Member.MID = List.MID)
WHERE (((Member.[Date_of_Birth]) Between #4/16/1995# And #4/16/1985#))
) as t
GROUP BY Genre_Name
ORDER BY COUNT(*) DESC;
I think this should work:
SELECT Genre.Genre_Name, count(Member.MID) as Number_of_People_aged_20_to_30
FROM
((((Genre
INNER JOIN Film_Actor_Director_Genre ON Genre.GID = Film_Actor_Director_Genre.GID)
INNER JOIN Film ON Film_Actor_Director_Genre.FID = Film.FID)
INNER JOIN List ON Film.FID = List.FID)
INNER JOIN Member ON Member.MID = List.MID)
WHERE (((Member.[Date_of_Birth]) Between #4/16/1995# And #4/16/1985#))
GROUP BY Genre.Genre_Name
ORDER BY count(Member.MID) DESC;