When would you INNER JOIN a LEFT JOINed table / - sql

I came across below code today.
SELECT StaffGroup.*
FROM StaffGroup
LEFT OUTER JOIN StaffByGroup
ON StaffByGroup.StaffGroupId = StaffGroup.StaffGroupId
INNER JOIN StaffMember
ON StaffMember.StaffMemberId = StaffByGroup.StaffMemberId
WHERE StaffByGroup.StaffGroupId IS NULL
The main table StaffGroup is being LEFT JOINed with StaffByGroup table and then StaffByGroup table is being INNER JOINed with StaffMember table.
I thought the INNER JOIN is trying to filter out the records which exist in StaffGroup and StaffByGroup but do not exist in StaffMember.
But this is not how it is working. The query does not return any records.
Am I missing something in understanding the logic of the query ? Have you ever used INNER JOIN with a table which has been used with LEFT JOIN in earlier part of the query ?

Actually you are missing one concept:
The main table StaffGroup is being LEFT Joined with StaffByGroup table and then this creates a virtual table say VT1 with all records from StaffGroup and matching records from StaffByGroup based on your match/filter condition in ON predicate.Then not StaffByGroup table but this VT1 is being INNER Joined with StaffMember table based on match/filter condition in ON predicate.
So basically the inner join is trying to filter out those records from StaffGroup and hence StaffByGroup which do not have a StaffMemberId.
Adding your where condition adds a final filter like from the final virtual table created by all the above joins remove all such records which don't have a StaffGroupId which in turn might be removing all rows collected in VT1 as all of them will be having some value for StaffGroupId.
To get all records from StaffGroup which have no StaffGroupId along with details from StaffMember for all such records you can add condition in ON predicate as:
SELECT StaffGroup.*
FROM StaffGroup
LEFT OUTER JOIN StaffByGroup
ON StaffByGroup.StaffGroupId = StaffGroup.StaffGroupId and StaffByGroup.StaffGroupId IS NULL
INNER JOIN StaffMember
ON StaffMember.StaffMemberId = StaffByGroup.StaffMemberId

This query looks fundamentally flawed - I guess what was originally intended is
SELECT StaffGroup.*
FROM StaffGroup
LEFT OUTER JOIN
(SELECT * FROM StaffByGroup
INNER JOIN StaffMember
ON StaffMember.StaffMemberId = StaffByGroup.StaffMemberId) StaffByGroup
ON StaffByGroup.StaffGroupId = StaffGroup.StaffGroupId
WHERE StaffByGroup.StaffGroupId IS NULL
which returns all groups from StaffGroup that dont' have existing staffmembers assigned to them (the INNER JOIN with StaffMember filters out those rows from StaffByGroup that don't have a matching row in StaffMember - probably because there exists no foreign key between them)

You are getting 0 records because of your where clause
where StaffByGroup.StaffGroupId is null
The left join links all the records from tbl A which are contained in tbl B and since you have specified StaffGROUPID as your key and then looked for Nulls values in your key, its 100% clear that you will end up with no results

Related

joining to a table that might not exist

So I have joined a table to itself in order to find the most recently added note of a certain type (;NoteTypesID=20625;). BUT when there is NOTHING of that type, it completely excludes the entire record's results from my dataset, instead of giving me nulls for the fields I am trying to pull from the ;PROPOSALNOTEPAD; table.
---select ... from ... (including the prop table)
left join PROPOSALNOTEPAD PN_SolicitationStrategy on prop.ID = PN_SolicitationStrategy.ParentId
join ( select parentID, max(DateAdded) as MaxDateAdded FROM PROPOSAL_NOTEPAD where NoteTypeID = 20625 group by ParentId) as PN_max_SolicitationStrategy
ON PN_SolicitationStrategy.ParentId = PN_max_SolicitationStrategy.ParentId AND PN_SolicitationStrategy.DateAdded = PN_max_SolicitationStrategy.MaxDateAdded
I need to be able to test if this is null: ;select parentID FROM PROPOSAL_NOTEPAD where NoteTypeID = 20625;. And then do my joins based on that result. How do I do that? How do I join based on a condition?
Use LEFT OUTER JOIN:
left join PROPOSALNOTEPAD PN_SolicitationStrategy on prop.ID = PN_SolicitationStrategy.ParentId
LEFT OUTER JOIN ( select parentID, max(DateAdded) as MaxDateAdded FROM PROPOSAL_NOTEPAD where NoteTypeID = 20625 group by ParentId) as PN_max_SolicitationStrategy
ON PN_SolicitationStrategy.ParentId = PN_max_SolicitationStrategy.ParentId AND PN_SolicitationStrategy.DateAdded = PN_max_SolicitationStrategy.MaxDateAdded
Inner Join always brings the common records from both table. In your case, there is no matching record from 1st table and PN_max_SolicitationStrategy table. That's why you are getting no records.
Using LEFT OUTER JOIN will bring you all the records from 1st table and matching records from the 2nd (PN_max_SolicitationStrategy) table.
Here is simple tutorial to learn about JOINs: https://www.w3schools.com/sql/sql_join_left.asp

Left join, where clause

I'm wondering why this returns no values but if I place the "and r2.ref_nm = 'memberPrograStatusType" in the join clause it returns values. The 'memberPrograStatusType is null.
`select mp.mbr_id
,r1.ref_desc as program_name
,r2.ref_desc as program_status
,mp.nom_dt
,mp.enrl_dt
,mp.end_dt
from icue.mbr_pgm mp
left join icue.ref r1 on mp.pgm_typ_id = r1.ref_cd
left join icue.ref r2 on mp.mbr_pgm_sts_typ_id = r2.ref_cd
where '15-JAN-17' between mp.enrl_dt and nvl(mp.end_dt,sysdate)
and mp.mbr_id = 46714641
and r1.ref_nm = 'programType'
and r2.ref_nm = 'memberPrograStatusType'
'
The reason is because when you place a filter from a column in the left join in the where clause, you are turning it into an inner join.
However if you place the filter on the join clause then the filter applies only to the rows that have data in left join table.
The key here is order of operations
WHERE is applied after JOIN
If you put r2.ref_nm = 'x' into the JOIN predicate, it is applied against the right table, which is then joined to the left table. Because you are using a LEFT JOIN, this means that the rows joined from the right table are filtered on r2.ref_nm = 'x', but all rows from the left table are still preserved in the result set.
If you put r2.ref_nm = 'x' into the WHERE clause, then it is applied to the entire result set, after the joins have occured. This essentially makes your LEFT JOIN into an INNER JOIN if your filter is looking for anything other than NULL
TLDR: if you want to filter on the right table in a LEFT JOIN, you have to do so in the join predicate, otherwise you will lose all the NULL values that are created from the LEFT JOIN

SQl query inner join to return the available data even if the join is missing

I am still messing around with this query, which is working but is not returning the data I need.
SELECT prod.Code,
prod.Description,
prod.Groupp,
prod.Quantity,
prod.Unit,
prod.Standard,
prod.Type,
prod.Model,
prod.GroupBKM,
prod.Note,
comp.Unit,
comp.Cost
FROM dbo.Product1 prod
INNER JOIN dbo.Components comp
ON comp.Code = prod.Code
The above query is returning the data only if a comp.code=prod.code exists while I would like to get the data prod.* in any case and obviously the data relevant comp.cost, if does not exist, will be null.
I cannot get it right! Any help will be appreciated.
Replace INNER JOIN with LEFT JOIN
SELECT prod.Code,
prod.Description,
prod.Groupp,
prod.Quantity,
prod.Unit,
prod.Standard,
prod.Type,
prod.Model,
prod.GroupBKM,
prod.Note,
comp.Unit,
comp.Cost
FROM dbo.Product1 prod
LEFT JOIN dbo.Components comp
ON comp.Code = prod.Code
By definition you cannot do this with an INNER JOIN because an INNER JOIN is defined as only returning items for which a match was found.
If you want to return rows in the base SELECT even if the JOIN predicate fails, then you want a LEFT OUTER JOIN ... which is defined as precisely that.
From Wikipedia:
An outer join does not require each record in the two joined tables to
have a matching record. The joined table retains each record—even if
no other matching record exists. Outer joins subdivide further into
left outer joins, right outer joins, and full outer joins, depending
on which table's rows are retained (left, right, or both).
In your case, replace INNER JOIN with LEFT OUTER JOIN.

Why does this query produce the same information?

I am using the Lyric Database. I am wondering why this query produces the same information:
SELECT M.LastName, A.ArtistName
FROM members M
LEFT JOIN XRefArtistsMembers X USING(MemberID)
JOIN Artists A on X.ArtistID = A.ArtistID;
And
SELECT M.LastName, A.ArtistName
FROM members M
INNER JOIN XRefArtistsMembers X USING(MemberID)
JOIN Artists A on X.ArtistID = A.ArtistID;
There are no errors. I can post relevent records from the tables by request.
INNER JOIN only chooses only those entries from the two joined tables, where the values from the fields in the JOIN clause match in both tables.
LEFT JOIN chooses all entries from the first table, combined with either the matched entries from the second table or with NULL values instead, if there was no match.
If every row from the first table had a match in the second table on the field mentioned in the JOIN clause, then both INNER JOIN and LEFT JOIN will produce the same results.

Combining OUTER JOIN and WHERE

I'm trying to fetch some data from a database.
I want to select an employee, and if available, all appointments and other data related to that employee.
This is the query:
SELECT
TA.id,
TEI.displayname,
TA.threatment_id,
TTS.appointment_date,
TEI.displayname
FROM
tblemployee AS TE
LEFT OUTER Join tblappointment AS TA ON TE.employeeid = TA.employee_id
Inner Join tblthreatment AS T ON TA.threatment_id = T.threatmentid
Inner Join tblappointments AS TTS ON TTS.id = TA.appointments_id AND
TTS.appointment_date = '2009-09-28'
INNER Join tblemployeeinfo AS TEI ON TEI.employeeinfoid = TE.employeeinfoid
Inner Join tblcustomercard AS TCC ON TCC.customercardid = TTS.customercard_id
WHERE
TE.employeeid = 4
The problem is, it just returns null for all fields selected when there are no appointments. What am I not getting here?
Edit:
For clearity, i removed some of the collumns. I removed one too many. TEI.displayname should at least be displayed.
Looking at the list of columns returned by your query, you will notice that they all come from the "right" side of the LEFT OUTER JOIN. You do not include any columns from the "left" side of the join. Therefore, the expected result is the one you are observing — NULL values supplied for all right-hand columns in the result set for those rows that have no right-hand rows returned.
To see data even for those rows, include some columns from TE (tblemployee) in the result set.
Looking at your query I'm guessing that the situation is a bit more complex and that some of those tables on the right-hand side of the join should be moved to the left-hand side and, furthermore, that some of the other tables might possibly require their own OUTER joins to participate correctly in the query.
Edited w/ response to questioner's comment:
You have an odd situation (maybe not odd at all, depending on your application) in which you have an employee table and a separate employee information (employeeinfo) table.
Because you are joining the employeeinfo to the appointments table with an INNER join you can effectively think of them as a single table in terms of how they contribute to the final result set. Because this combined table REQUIRES a record in the appointments table and because this combined table is joined into the main result set with a LEFT OUTER join, the effect is that the employeeinfo record is not found if there's no appointment to link it to.
If you move the employeeinfo table to the left side of the join, or replace the employee table w/ the employeeinfo table, you should get the results you want.
In your query, you LEFT OUTER JOIN to the tblappointment table, but then you INNER JOIN to the tblthreatment and tblappointments tables.
You should try and structure your query in the order that you expect data to be there. Then in most simple queries, once you perform an OUTER join, most tables after that will be an OUTER join. This is by NO MEANS a rule and complex queries can vary, but in the marjority of simple queries its a good practice.
Try something like this for your query.
SELECT
TA.id,
TEI.displayname,
TA.threatment_id,
TTS.appointment_date
FROM
tblemployee AS TE
INNER Join
tblemployeeinfo AS TEI
ON
TEI.employeeinfoid = TE.employeeinfoid
LEFT OUTER Join
tblappointment AS TA
ON
TE.employeeid = TA.employee_id
LEFT OUTER JOIN
tblthreatment AS T
ON
TA.threatment_id = T.threatmentid
LEFT OUTER JOIN
tblappointments AS TTS
ON
TTS.id = TA.appointments_id
AND
TTS.appointment_date = '2009-09-28'
LEFT OUTER JOIN
tblcustomercard AS TCC
ON
TCC.customercardid = TTS.customercard_id
WHERE
TE.employeeid = 4
The issue is that the way you're joining (most of everything is joining to your left outer-joined table) whenever you're joining off of that, if the value in the outer joined table is nothing, there is nothing for the other fields to join to. Try to re-adjust your query so everything is joining off of your employeeID. I normally use left joined tables after I've limited everything down as much as possible with inner joins.
So my query would be something like:
SELECT
TA.id,
TEI.displayname,
TA.threatment_id,
TTS.appointment_date
FROM
tblemployee AS TE
INNER Join tblemployeeinfo AS TEI ON TEI.employeeinfoid = TE.employeeinfoid
Inner Join tblthreatment AS T ON TA.threatment_id = T.threatmentid
Inner Join tblappointments AS TTS ON TTS.id = TA.appointments_id AND
TTS.appointment_date = '2009-09-28'
Inner Join tblcustomercard AS TCC ON TCC.customercardid = TTS.customercard_id
LEFT OUTER Join tblappointment AS TA ON TE.employeeid = TA.employee_id
WHERE
TE.employeeid = 4
where the last outer join just gives me one column worth of information, not using it all to join more things onto. For speed, you also want to try to limit your information down as fast as possible with your first few inner joins, and then you do the outer joins last to join possible null values on to the smallest dataset you can. I hope this helps, if it's confusing, I'm sorry... I haven't had my caffeine yet.
The query is performing as it should.
A left out join will select all records from one table, join them with the records in another, and produce nulls where no records in the second table are found that match the join condition.
If you're looking for a separate behavior, you may want to think about two separate queries.