Left outer join with count, on 3 tables not returning all rows from left table - sql

I have these 3 tables:
Areas - id, name
Persons - id, area_id
Special_Persons - id_person, date
I'd like to produce a list of all Areas, followed by a count of Special Persons in each area, including Areas with no Special Persons.
If I do a left join of Areas and Persons, like this:
select a.id as idArea, count(p.id) as count
from areas a
left join persons p on p.area_id = a.id
group by a.id;
This works just fine; Areas that have no Persons show up, and have a count of 0.
What I am not clear on is how to do the same thing with the special_persons table, which currently only has 2 entries, both in the same Area.
I have tried the following:
select a.id as idArea, count(sp.id_person) as count
from special_persons sp, areas a
left join persons p on p.area_id = a.id
where p.area_id = a.id
and sp.id_person = p.id
group by a.id;
And it only returns 1 row, with the Area that happens to have 2 Special Persons in it, and a count of 2.
To continue getting a list of all areas, do I need to use a sub-query? Another join? I'm not sure how to go about it.

You can add another left join to the Special_Persons table:
select a.id as idArea, count(p.id), count(sp.id_person)
from areas a
left join persons p on p.area_id = a.id
left join special_persons sp on sp.id_person = p.id
group by a.id;

Related

SQL List all of MemberID's of people who arent in the ones listed

Outputted Date
I have a seperate Members table which has all the members ID's and I want to list all those but get rid of the ones that are displayed in this list.
SELECT DISTINCT tbl_classregistration.ClassID, tbl_classregistration.MemberID
FROM tbl_member INNER JOIN (tbl_classes
INNER JOIN tbl_classregistration ON
tbl_classes.ClassID = tbl_classregistration.ClassID) ON
tbl_member.MemberID = tbl_classregistration.MemberID
GROUP BY tbl_classregistration.ClassID, tbl_classregistration.MemberID
HAVING (((tbl_classregistration.ClassID)=[Enter ClassID]));
Thats the SQL View
Use not in:
select memberid from members where memberid not in (SELECT DISTINCT tbl_classregistration.MemberID
FROM tbl_member INNER JOIN (tbl_classes INNER JOIN tbl_classregistration ON tbl_classes.ClassID = tbl_classregistration.ClassID) ON tbl_member.MemberID = tbl_classregistration.MemberID
GROUP BY tbl_classregistration.ClassID, tbl_classregistration.MemberID
HAVING (((tbl_classregistration.ClassID)=[Enter ClassID])))

sql returning three time the same column

I'm having troubles trying to select three times the user name.
I have a table
JOBS (id, id_student, id_professor, id_professor2)
This last column is optional, then sometimes NULL is set in its content. When that happens, the query doesn't return any rows. However, if id_professor2 is set, the query returns like I expect. How can I fix this?
SELECT
p.name as professor, s.name as student, s_p.name as second_professor
FROM
users as p
JOIN
jobs ON (p.id = jobs.id_professor)
JOIN
users as s ON (jobs.id_student = s.id)
JOIN
users as s_p ON (s_p.id = jobs.id_professor2)
WHERE
jobs.id = 2;
The job with id=2 has an second professor, so the query runs normally. But if a change for jobs.id=3 the query doesn't return anything.
Use a left outer join
SELECT p.name as professor, a.name as student, s_p.name as second_professor
FROM users as p
JOIN jobs ON(p.id = jobs.id_professor)
JOIN users as s ON(jobs.id_student = a.id)
left outer
JOIN users as c ON(c.id = jobs.id_professor2)
WHERE jobs.id = 2
;

Getting individual counts of a tables column after joining other tables

I'm having problems getting an accurate count of a column after joining others. When a column is joined I would still like to have a DISTINCT count of the table that it is being joined on.
A restaurant has multiple meals, meals have multiple food groups, food groups have multiple ingredients.
Through the restaurants id I want to be able to calculate how many of meals, food groups, and ingrediants the restaurant has.
When I join the food_groups the count for meals increases as well (I understand this is natural behavior I just don't understand how to get what I need due to it.) I have tried DISTINCT and other things I have found, but nothing seems to do the trick. I would like to keep this to one query rather than splitting it up into multiple ones.
SELECT
COUNT(meals.id) AS countMeals,
COUNT(food_groups.id) AS countGroups,
COUNT(ingrediants.id) AS countIngrediants
FROM
restaurants
INNER JOIN
meals ON restaurants.id = meals.restaurant_id
INNER JOIN
food_groups ON meals.id = food_groups.meal_id
INNER JOIN
ingrediants ON food_groups.id = ingrediants.food_group_id
WHERE
restaurants.id='43'
GROUP BY
restaurants.id
Thanks!
The DISTINCT goes inside the count
SELECT
COUNT(DISTINCT meals.id) AS countMeals,
COUNT(DISTINCT food_groups.id) AS countGroups,
COUNT(DISTINCT ingrediants.id) AS countIngrediants
FROM
restaurants
INNER JOIN
meals ON restaurants.id = meals.restaurant_id
INNER JOIN
food_groups ON meals.id = food_groups.meal_id
INNER JOIN
ingrediants ON food_groups.id = ingrediants.food_group_id
WHERE
restaurants.id='43'
GROUP BY
restaurants.id
You're going to have to do subqueries, I think. Something like:
SELECT
(SELECT COUNT(1) FROM meals m WHERE m.restaurant_id = r.id) AS countMeals,
(SELECT COUNT(1) FROM food_groups fg WHERE fg.meal_id = m.id) AS countGroups,
(SELECT COUNT(1) FROM ingrediants i WHERE i.food_group_id = fg.id) AS countGroups
FROM restaurants r
Where were you putting your DISTINCT and on which columns? When using COUNT() you need to do the distinct inside the parentheses and you need to do it over a single column that is distinct for what you're trying to count. For example:
SELECT
COUNT(DISTINCT M.id) AS count_meals,
COUNT(DISTINCT FG.id) AS count_food_groups,
COUNT(DISTINCT I.id) AS count_ingredients
FROM
Restaurants R
INNER JOIN Meals M ON M.restaurant_id = R.id
INNER JOIN Food_Groups FG ON FG.meal_id = M.id
INNER JOIN Ingredients I ON I.food_group_id = FG.id
WHERE
R.id='43'
Since you're selecting for a single restaurant, you shouldn't need the GROUP BY. Also, unless this is in a non-English language, I think you misspelled ingredients.

Need help with a simple Join

Oi
Right to the problem.
SELECT *,t.id AS threadid FROM threads t
LEFT JOIN players p on p.id = t.last_poster
WHERE t.boardid = $boardid
I have two fields in threads called posterid and lastposterid. Which are the IDs of the thread starter / last poster. What I want to do is to get their names from players table.
But how?
You just need to join to your players table twice, like this.
SELECT
threads.*,
starterPlayer.*,
lastPosterPlayer.*
FROM
threads
LEFT OUTER JOIN
players starterPlayer
ON
starterPlayer.id = threads.posterid
LEFT OUTER JOIN
players lastPosterPlayer
ON
lastPosterPlayer.id = threads.lastposterid
You can join to the same table twice and give the table a different alias.
This presumes that there always will be a first and last poster, if this is the case then you want an INNER JOIN rather than a LEFT JOIN, you will need to change the select statement to get the relevant fields.
SELECT t.id AS threadid, playerFirst.name AS FirstPoster, playerLast.name as LastPoster
FROM threads t
INNER JOIN
players playerFirst ON playerFirst.id = t.posterid
INNER JOIN
players playerLast ON playerLast.id = t.lastposterid
How about...
SELECT *,
(SELECT name
FROM players
WHERE players.id = threads.posterid) AS poster,
(SELECT name
FROM players
WHERE players.id = threads.lastposterid) AS last_poster
FROM threads;

SQL Join question

I have 3 tables
Links
Link ID
Link Name
GroupID (FK into Groups)
SubGroupID (FK into Subgroups)
Groups
GroupID
GroupName
SubGroup
SubGroupID
SubGroupName
GroupID (FK into Groups)
Every link needs to have a GroupID but teh SubGroupID is optional. How do i write a SQL query to show:
Links.LinkName, Groups.GroupName, SubGroup.SubGroupName
For the records with no subgroup just put a blank entry in that field. If i have 250 link rows, i should get back 250 reecords from this query.
Is there a way to do this in one query or do i need to do multiple queries?
This assumes that there is at most only 1 subgroup per group. if there are more, then you have the potential to get additional records.
select links.linkname, groups.groupname, subgroup.subgroupname
from links
inner join groups on (links.groupid = groups.groupid)
left outer join subgroup on (links.subgroupid = subgroup.subgroupid)
SELECT Links.LinkName, Groups.GroupName, SubGroup.SubGroupName -- Will potentially be NULL
FROM Links
INNER JOIN Groups
ON Group.GroupID = Links.GroupID
LEFT JOIN SubGroup
ON SubGroup.SubGroupID = Links.SubGroupID
You would use an Outer Join:
select Links.LinkName, Groups.GroupName, SubGroup.SubGroupName
from Links
inner join Groups on Groups.GroupID = Links.GroupID
left outer join SubGroup on Links.SubGroupID = SubGroup.SubGroupID
SELECT
links.linkname
, groups.groupname
, subgroup.groupname
FROM
links
JOIN groups ON links.groupid = groups.groupid
LEFT OUTER JOIN subgroups ON links.subgroupid = subgroup.subgroupid
(re-added to address OP's question)
incidentally, why not keep groups and subgroups in the same table, and use a self-referential join?
Akantro:
You'd have something like this:
create table groups(
groupid integer primary key,
parentgroupid integer foreign key references groups (groupid),
groupname varchar(50))
your query would then be
SELECT
links.linkname
, groups.groupname
, SUBGROUPS.groupname
FROM
links
JOIN groups ON links.groupid = groups.groupid
LEFT OUTER JOIN groups SUBGROUPS ON links.subgroupid = subgroup.groupid
there's no functional difference to keeping the tables like this, but the benefit is you only have to go to one place to edit the groups/subgroups
You're not too clear, but I think you want to get all rows including those that don't have a correspondent in the SubGroup table.
For this you can use LEFT JOIN, it will fetch NULLs if there are no matching rows.
SELECT LinkName, GroupName, SubGroupNamne
FROM Links INNER JOIN Groups ON LInks.GroupID = Groups.GroupID
LEFT JOIN SubGroup ON Links.SubGroupID = SubGroup.SubGroupID
This will include rows that do not have a sub group. That column will simply be NULL.
select L1.LinkName, G1.GroupName, NVL(S1.SubGroupName,' ')
from Links L1, Groups G1, SubGroup S1
where L1.GroupID = G1.GroupID and
L1.GroupID = S1.GroupID
Okay, try:
select a.linkname, b.groupname, c.subgroupname
from links a, groups b, subgroup c
where a.groupid = b.groupid
and a.subgroupid = c.subgroupid
and a.subgroupid is not null
union all
select a.linkname, b.groupname, ' '
from links a, groups b
where a.groupid = b.groupid
and a.subgroupid is null
I think that should work (it does in DB2 which is the DBMS I use most) - you'll need to adjust the spaces in the second select to match the subgroup.subgroupname size.
Use a LEFT OUTER JOIN on the SubGroup table will give you all rows from the Links table and where a SubGroup exists will return that otherwise you see a NULL value.
SELECT L.LinkName, G.GroupName, S.SubGroupName
FROM Links As L
INNER JOIN Groups As G ON L.GroupID=G.GroupID
LEFT OUTER JOIN SubGroup S ON L.SubGroupID=S.SubGroupID
This does not check that your SubGroups.LinkID matches the Links.LinkID which should never happen but if you need to check this then add in another clause to the join:
SELECT L.LinkName, G.GroupName, S.SubGroupName
FROM Links As L
INNER JOIN Groups As G ON L.GroupID=G.GroupID
LEFT OUTER JOIN SubGroup S ON L.SubGroupID=S.SubGroupID AND L.GroupID=S.GroupID
Just use a LEFT OUTER JOIN on the SubGroup table like:
select
l.LinkName,
g.GroupName,
s.SubGroupName
from
Links l
'
JOIN Group g
on ( g.GroupId = l.GroupId)
'
LEFT OUTER JOIN SubGroup s
on ( s.SubGroupId = l.SubGroupId )
That should do it.