Left join, where clause - sql

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

Related

Using inner or outer join based on input parameter - without using dynamic sql

It is possible to condition a join to outer or inner according to the value of a parameter without using dynamic sql?
I mean, if a parameter(filter value) is given then the query must
return exactly matching records (or 0 records) - acting as inner join
If this filter is not provided then it is needed to return all
records - acting as an outer join
If the filter is on the table you're outer joining to
select some_columns
from left l
left outer join right r
on( l.right_id = r.right_id )
where r.filter_column = p_some_parameter
or p_some_parameter is null
would seem to be all you need. If p_some_parameter is specified, the predicate effectively turns the outer join into an inner join. If p_some_parameter is not specified, the query remains an outer join.

Excluding LEFT OUTER JOINS in an Oracle SQL QUERY if a COLUMN on base table = 'N'

Below you'll see a simple JOIN statement of 5 base tables, I'm trying to excluded the two LEFT OUTER JOIN statements tables (dandm_emi_cae_statement_links, geod_us_cae_statement_links) from the SQL statement if the column geod_exclude_us_y in the JMD_UDG_DISTRIBUTION table is equal 'N'.
Currently the below statement in the SQL PLAN still looks at these tables, how do I stop it from doing the LEFT OUTER JOIN' if the table JMD_UDG_DISTRIBUTION column geod_exclude_us_y = 'N'?
SELECT lfu.*
FROM JMD_UDG_DISTRIBUTION jud
JOIN dm_lifeline_file_usages lfu
ON lfu.group_id = jud.GROUP_SURR_ID
JOIN dm_lifeline_file_ips lfi
ON lfi.usage_group_id = lfu.group_id
AND lfi.lfu_surr_id = lfu.surr_id
LEFT OUTER JOIN dandm_emi_cae_statement_links dnm
ON jud.geod_exclude_us_y = 'Y'
AND dnm.cae=lfi.cae_number
LEFT OUTER JOIN geod_us_cae_statement_links gucs
ON jud.geod_exclude_us_y = 'Y'
AND gucs.cae_number=lfi.cae_number
Of course. The rules for LEFT JOIN are:
All the rows that match the ON condition.
All rows in the first table when there is no match in the second.
So, a condition on the first table in ON has no impact on the results. That is not strictly true. There is a weird sort of impact. The columns from the second table -- even for otherwise matching rows -- are NULL.
So, you want to filter in the WHERE clause. Move jud.geod_exclude_us_y = 'Y' from the ON clauses to:
WHERE jud.geod_exclude_us_y = 'Y'

returned no of rows different on left join

i have two sql query in one of them i perform left outer join, both should return same no of records but returned no of rows are different in both the sql queries
select Txn.txnRecNo
from Txn
inner join Person on Txn.uwId = Person.personId
full outer join TxnInsured on Txn.txnRecNo = TxnInsured.txnRecNo
left join TxnAdditionalInsured on Txn.txnRecNo = TxnAdditionalInsured.txnRecNo
where Txn.visibleFlag=1
and Txn.workingCopy=1
returned 20 records
select Txn.txnRecNo
from Txn
inner join Person on Txn.uwId = Person.personId
full outer join TxnInsured on Txn.txnRecNo = TxnInsured.txnRecNo
where Txn.visibleFlag=1
and Txn.workingCopy=1
returned 15 records
I suspect that the TxnAdditionalInsured table have duplicate records. use distinct
select distinct Txn.txnRecNo
from Txn
inner join Person on Txn.uwId = Person.personId
full outer join TxnInsured on Txn.txnRecNo = TxnInsured.txnRecNo
left join TxnAdditionalInsured on Txn.txnRecNo = TxnAdditionalInsured.txnRecNo
where Txn.visibleFlag=1
and Txn.workingCopy=1
A left join will produce all rows from the left side of the join at least once in the result set.
But if your join conditions are such that there are multiple rows from the right side that match a particular row on the left, that left row will appear multiple times in the result (as many times as it is matched with a right row).
So, if the results are unexpected, your join criteria aren't are strict as they need to be or you do not understand your data as well as you thought you did.
Unlike the other answers, I would not suggest just adding distinct - I'd suggest you investigate your data and determine whether your ON clause needs strengthening or if your data is in fact incorrect. Adding distinct to "make the results look right" is usually a poor decision - prefer to investigate and get the correct query written.
Try this:
select distinct Txn.txnRecNo --> added distinct here
from Txn
inner join Person on Txn.uwId = Person.personId
full outer join TxnInsured on Txn.txnRecNo = TxnInsured.txnRecNo
left join TxnAdditionalInsured on Txn.txnRecNo = TxnAdditionalInsured.txnRecNo
where Txn.visibleFlag=1
and Txn.workingCopy=1

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.

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.