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

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.

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.

Duplicate results due to multiple left outer joins

My query is as below
select DISTINCT
wftransaction.PERSONID,
pr.PRNUM,
pr.DESCRIPTION,
pr.PR1,
prline.GLDEBITACCT,
wftransaction.TRANSDATE,
prstatus.CHANGEBY
prstatus.CHANGEDATE,
prstatus.STATUS,
prstatus.MEMO
from pr
left outer join wftransaction pr.PRID = wftransaction.ORNERID and wftransaction.OWNERTABLE ='PR'
left outer join prline on pr.PRNUM = prline.PRNUM
left outer join prstatus on pr.PRNUM= prstatus.PRNUM
The result given by my query has duplicate results.Please do help me eliminate the redundant/repeating outputs.
When I put distinct this is what happens, https://i.stack.imgur.com/I2jnN.jpg,
I should only see 2 outputs with the same "STATUS" i.e.(COMPOSING) or (APPR) since they have different "GLDEBITACCT", other than that, there should be no more duplicates.
This is the picture of my Code and Result Set
i think you should be using inner join because if left join where being used all data from table A will repeatedly shows as the table B has its foreign key, or might sometimes you are lacking of WHERE clauses it depends on your query, it will be more helpful to others if you can paste the whole query and their structures with expected results.
https://www.codeproject.com/kb/database/visual_sql_joins.aspx
Good idea will be to place a simple DISTINCT clause in the query
select DISTINCT
wftransaction.PERSONID,
pr.PRNUM,
pr.DESCRIPTION,
pr.PR1,
prline.GLDEBITACCT,
wftransaction.TRANSDATE,
prstatus.CHANGEBY
prstatus.CHANGEDATE,
prstatus.STATUS,
prstatus.MEMO
from pr
left outer join wftransaction pr.PRID = wftransaction.ORNERID and wftransaction.OWNERTABLE ='PR'
left outer join prline on pr.PRNUM = prline.PRNUM
left outer join prstatus on pr.PRNUM= prstatus.PRNUM

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

When I add a LEFT OUTER JOIN, the query returns only a few rows

The original query returns 160k rows. When I add the LEFT OUTER JOIN:
LEFT OUTER JOIN Table_Z Z WITH (NOLOCK) ON A.Id = Z.Id
the query returns only 150 rows. I'm not sure what I'm doing wrong.
All I need to do is add a column to the query, which will bring back a code from a different table. The code could be a number or a NULL. I still have to display NULL, hence the reason for the LEFT join. They should join on the "id" columns.
SELECT <lots of stuff> + the new column that I need (called "code").
FROM
dbo.Table_A A WITH (NOLOCK)
INNER JOIN
dbo.Table_B B WITH (NOLOCK) ON A.Id = B.Id AND A.version = B.version
--this is where I added the LEFT OUTER JOIN. with it, the query returns 150 rows, without it, 160k rows.
LEFT OUTER JOIN
Table_Z Z WITH (NOLOCK) ON A.Id = Z.Id
LEFT OUTER JOIN
Table_E E WITH (NOLOCK) ON A.agent = E.agent
LEFT OUTER JOIN
Table_D D WITH (NOLOCK) ON E.location = D.location
AND E.type = 'Organization'
AND D.af_type = 'agent_location'
INNER JOIN
(SELECT X , MAX(Version) AS MaxVersion
FROM LocalTable WITH (NOLOCK)
GROUP BY agemt) P ON E.agent = P.location AND E.Version = P.MaxVersion
Does anyone have any idea what could be causing the issue?
When you perform a LEFT OUTER JOIN between tables A and E, you are maintaining your original set of data from A. That is to say, there is no data, or lack of data, in table E that can reduce the number of rows in your query.
However, when you then perform an INNER JOIN between E and P at the bottom, you are indeed opening yourself up to the possibility of reducing the number of rows returned. This will treat your subsequent LEFT OUTER JOINs like INNER JOINs.
Now, without your exact schema and a set of data to test against, this may or may not be the exact issue you are experiencing. Still, as a general rule, always put your INNER JOINs before your OUTER JOINs. It can make writing queries like this much, much easier. Your most restrictive joins come first, and then you won't have to worry about breaking any of your outer joins later on.
As a quick fix, try changing your last join to P to a LEFT OUTER JOIN, just to see if the Z join works.
You have to be very careful once you start with LEFT JOINs.
Let's suppose this model: You have tables Products, Orders and Customers. Not all products necessarily have been ordered, but every order must have customer entered.
Task: Show all products, and if the product was ordered, list the ordering customers; i.e., product without orders will be shown as one row, product with 10 orders will have 10 rows in the resultset. This calls for a query designed around FROM Products LEFT JOIN Orders.
Now someone could think "OK, Customer is always entered into orders, so I can make inner join from orders to customers". Wrong. Since the table Customers is joined through left-joined table Orders, it has to be left-joined itself... otherwise the inner join will propagate into the previous level(s) and as a result, you will lose all products that have no orders.
That is, once you join any table using LEFT JOIN, any subsequent tables that are joined through this table, need to keep LEFT JOINs. But it does not mean that once you use LEFT JOIN, all joins have to be of that type... only those that are dependent on the first performed LEFT JOIN. It would be perfectly fine to INNER JOIN the table Products with another table Category for example, if you only want to see Products which have a category set.
(Answer is based on this answer: http://www.sqlservercentral.com/Forums/Topic247971-8-1.aspx -> last entry)

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.