about left outer join sql - sql

So here's the scenario, I have two tables say payment and receipt with fields
payment_date, payment_amount, payment_party_code
and similarly
receipt_date, receipt_amount, receipt_party_code.
I have 4 rows in payment table, and 11 rows in receipt table.
What I want to do is select all rows from payment as well as from receipt table where the party_code is same. I did by using left outer join but failed because it is doing cartesian product of data.
Please help me out with this
Thanks

If you want ONLY the records that match on the payment_party_code, you should use an "INNER JOIN". But as marc_s said, you need to make sure that you specify your join criteria in your "ON" clause.

You need to use a LEFT OUTER JOIN and define a JOIN condition on it:
SELECT
p.payment_date, p.payment_amount, p.payment_party_code,
r.receipt_date, r.receipt_amount, r.receipt_party_code
FROM
dbo.Payment p
LEFT OUTER JOIN
dbo.Receipt r ON p.payment_party_code = r.receipt_party_code
This is for SQL Server (T-SQL). If you leave out the JOIN condition (the ON p.payment_party_code = r.receipt_party_code part), you'll get a cartesian product.
This will list all rows from the Payment table, and if they have info in the Receipt table, that'll be displayed as well.
If you want the opposite (everything from Receipt, even if there's no corresponding row in Payment for it), you need to either switch around the tables in the FROM ... LEFT OUTER JOIN (select from Receipt first), or you need to use a RIGHT OUTER JOIN so that you'll get everything from Receipt.
You might want to look at the Venn diagrams of SQL JOINs posted by Jeff Atwood - makes it quite clear what the different JOIN types really do...

Left Outer Join
The Left Outer Join logical operator returns each row that satisfies the join of the first (top) input with the second (bottom) input. It also returns any rows from the first input that had no matching rows in the second input. The nonmatching rows in the second input are returned as null values. If no join predicate exists in the Argument column, each row is a matching row
SELECT
pay.payment_date, pay.payment_amount, pay.payment_party_code,
rec.receipt_date, rec.receipt_amount, rec.receipt_party_code
FROM
payment pay
LEFT OUTER JOIN
receipt rec ON pay.payment_party_code = rec.receipt_party_code

select *
from party p
left outer join receipt r on p.party_code = r.party_code

Related

Join ON order in SQL

I have a question which is similar but not quite the same to others I've seen in on stackoverflow
Let's say I have the following query
select *
from BobsFriends bf
left outer join JennysFriends jf on bf.friendID = jf.friendID
does bf.friendID = jf.friendID and jf.friendID = bf.friendID return different results? I would think that these would return the same result but
select *
from JennysFriends bf
left outer join BobsFriends jf on bf.friendID = jf.friendID
would return different ones.
The order of the columns when comparing for the JOIN doesn't matter, but when doing an OUTER JOIN the order of the tables definitely matters.
For a LEFT OUTER JOIN you are going to always get the results from the first table, with data from the second table, if available.
For a RIGHT OUTER JOIN you are always going to get the results from the second table, with data from the first table, if available.
Left outer join produces a complete set of records from Table A (the "left side" table), with the matching records (where available) in Table B (the "right side" one). If there is no match, the right side will contain null.

sql left join criteria in join vs where clause

I have an existing query to select some payments
I want to filter out any payments that are for clients that have an active alert in another table called ClientAlert
So I figured I would do a left join and check if the ClientAlertId is null.
select *
from payments p
left join client c on c.clientid = p.clientid
left join ClientAlert ca on ca.CRMId = c.CRMId and ca.ClientAlertSubjectId = 1 and ca.IsActive = 1 and (ca.ExpiryDate is null or ca.ExpiryDate > GetDate())
where
ca.clientalertid is null and
p.PaymentStatusId = 2 and
p.PaymentDate <= GetDate() and
p.PaymentCategoryId = 1
This seems to work I think
But I have two questions:
Could there ever be a scenario that would cause multiple payments to be returned instead of one by adding this join?
when I specified the following in the where clause instead of the join, it did not give the same results and I dont understand why
and ca.ClientAlertSubjectId = 1 and ca.IsActive = 1 and (ca.ExpiryDate is null or ExpiryDate > GetDate())
I thought having that criteria in the where clause woiuld be equivelent to having it in the join
You could get multiples per payment record if it links to more than one Client record. Based on the WHERE clause though, I don't see how multiple ClientAlert records could cause duplication.
LEFT JOIN records return NULLs across all their columns when there is no match. Adding ca.ClientAlertSubjectId = 1 and ca.IsActive = 1 to the WHERE clause basically forces that join to behave like an INNER JOIN because it would HAVE to find a matching record, but I'm guessing it would never return data because ClientAlertId is a non-nullable column. So basically you created a query where you need a NULL row (indicating there are no alerts), but the row must contain data.
If they can have multiple alerts, theoretically. However since you are excluding payments with alerts, this should not be a problem. If you were including them it could be. If this was a problem, you should use a "not in" subquery instead of left outer join since that can cause duplicate records if it's not 1:1.
Having criteria in the where clause excludes the entire row if it doesn't match the criteria. Having it in the join clause means the joined record is not shown but the "parent" is.
select *
from payments p
left join client c on c.clientid = p.clientid
left join ClientAlert ca on ca.CRMId = c.CRMId
and ca.ClientAlertSubjectId = 1 and ca.IsActive = 1
and (ca.ExpiryDate is null or ca.ExpiryDate > GetDate())
where
ca.CRMId is null and
p.PaymentStatusId = 2 and
p.PaymentDate <= GetDate() and
p.PaymentCategoryId = 1
This tiny change will assure you will never get any duplicates IF clientid is unique in table client
Moving conditions from left join to where means you are moving the left join condition into a condition for the row. So if the condition is not met, the row is not returned.
Left join condition will always include the row from left side of the join
First, unless there are payments that aren't associated with a client, then the first join should be an inner join.
Second, once a client has the proper alert, you filter out all payments made by that client, even those made months or years before the alert took effect. Is that what you want?
Third and last (though this is a long one): an outer join has this format:
select ...
from InnerTable i
left [outer] join OuterTable o
on <join criteria>
where <filter criteria>;
For outer joins only, it is important that all checks involving the outer table should be consider as join criteria. At a bear minimum, there will be
on o.JoinField = i.OtherJoinField
or in your case
on ca.CRMId = c.CRMId
To keep the output that is expected of an outer join, checks of other outer table fields should also go in the ON clause as additional join criteria:
on ca.CRMId = c.CRMId
and ca.ClientAlertSubjectId = 1
and ca.IsActive = 1
and (ca.ExpiryDate is null or ca.ExpiryDate > GetDate())
Adding those addition checks to the WHERE clause will, as you discovered, completely filter out all the benefits of the outer join to deliver the same result set as an inner join.
However, you could get the output to return to the outer join result set by making one little change:
where (ca.clientalertid is null
or (ca.ClientAlertSubjectId = 1
and ca.IsActive = 1
and (ca.ExpiryDate is null or ca.ExpiryDate > GetDate()))
and p.PaymentStatusId = 2
and ...
But this doesn't quite get back to a normal outer join result set. In your case, the regular outer join will show payment and client data with NULLs for alert data for:
Payments by clients with no alerts at all.
Payments by clients with alerts but not fully qualified by the additional criteria. For example, if IsActive contains 0.
Then there would be alert data for those payments by clients who have a fully qualified alert.
Moving the additional criteria to the WHERE clause but in the format shown above will show payment and client data with NULLs for alert data only for payments with no alerts at all, along with alert data for fully qualified alerts. The payments and clients with alerts not fully qualified would not show up at all.
Remember that. There may be times when that is exactly what you want.
But that does not appear to be what you want at this time. If you want to see no alerts and partial alerts but filter out the fully qualified alerts, you can't place the other criteria in the WHERE clause. Your original query is the only way to do that.
There really is no reason to be placing the additional criteria in the WHERE clause. It gets you nothing. There is no performance benefit to be gained from it even for the inner join where it doesn't effect the output. Compare the execution plan for the inner joins with the additional criteria in the ON clause and in the WHERE clause. They are identical. The same comparison for outer joins are different. In fact, with the addition criteria in the WHERE clause, the execution plan is identical to those of the inner joins.
So just use the query you have and be satisfied that you are getting the output you want.

SQL to query rows that do not have a value

I am trying to query all rows of a "parts" table even if their number ordered is none. This is giving me a problem because when the number ordered is not on the "order_line" table it does not show the item as having none (which is essentially the reason for this question in the assignment)
Here is my sql for to find all of the parts with number ordered:
SELECT PART.PART_NUM,
PART.DESCRIPTION,
PART.ON_HAND,
PART.PRICE,
ORDERS.ORDER_NUM,
ORDER_LINE.NUM_ORDERED
FROM PART
INNER JOIN ORDER_LINE
ON PART.PART_NUM = ORDER_LINE.PART_NUM
INNER JOIN ORDERS
ON ORDER_LINE.ORDER_NUM = ORDERS.ORDER_NUM
ORDER BY PART.PART_NUM
This is for school so if you could just point me in the right direction that would be great. Hope this made sense..
use 'left outer join' in place of 'inner join' for the order_line table
EDIT: as pointed out by Martin, you need to replace both 'inner join's with 'left outer join'
As you can see, when you specify INNER JOIN it means that for a row to be in the result set it must be made up of an existing row from the left-hand side of the JOIN and an existing row from the right-hand side.
But INNER JOIN is not the only way to JOIN tables. You can also use LEFT or RIGHT OUTER JOIN, which requires only one of the two tables to have a row in it (the values that would have come from the other side of the JOIN are all NULL in the result set). There is even a FULL OUTER JOIN, which requires a row from either of the two sides.
Look up OUTER JOIN in your documentation for more information.

SQL DB Question

Question about SQL View. Trying to develop a view from two tables. The two tables have same Primary Keys, execpt the 1st table has all of them, the 2nd has some, but not all. When I INNER Join them, I get a recordset but its not complete, because the 2nd table doesnt have all the records in it. Is there a way in my view to write logic stating that if the key isnt in there int he table #2 to insert a zero so the entire record set is shown in the view? I wan tto show ALL the records in the view even if theres nothing to inner join.
My example below:
SELECT dbo.Baan_view1b.Number, dbo.Baan_view1b.description, dbo.Baan_view1b.system, dbo.Baan_view1b.Analyst, dbo.Baan_view1b.[User],
dbo.Baan_view1b.[Date Submitted], dbo.Baan_view1b.category, dbo.Baan_view1b.stage, MAX(dbo.notes.percent_developed) AS Expr1
FROM dbo.Baan_view1b INNER JOIN
dbo.notes ON dbo.Baan_view1b.Number = dbo.notes.note_number
GROUP BY dbo.Baan_view1b.Number, dbo.Baan_view1b.description, dbo.Baan_view1b.system, dbo.Baan_view1b.Analyst, dbo.Baan_view1b.[User],
dbo.Baan_view1b.[Date Submitted], dbo.Baan_view1b.category, dbo.Baan_view1b.stage
HAVING (NOT (dbo.Baan_view1b.stage LIKE 'Closed'))
what you are looking for is the Left Join (left outer join) and not the inner join
SELECT dbo.Baan_view1b.Number, dbo.Baan_view1b.description, dbo.Baan_view1b.system, dbo.Baan_view1b.Analyst,
dbo.Baan_view1b.[User], dbo.Baan_view1b.[Date Submitted], dbo.Baan_view1b.category, dbo.Baan_view1b.stage,
MAX(dbo.notes.percent_developed) AS Expr1
FROM dbo.Baan_view1b
LEFT OUTER JOIN dbo.notes
ON dbo.Baan_view1b.Number = dbo.notes.note_number
WHERE NOT dbo.Baan_view1b.stage LIKE 'Closed'
GROUP BY dbo.Baan_view1b.Number, dbo.Baan_view1b.description, dbo.Baan_view1b.system, dbo.Baan_view1b.Analyst,
dbo.Baan_view1b.[User], dbo.Baan_view1b.[Date Submitted], dbo.Baan_view1b.category, dbo.Baan_view1b.stage
Also, changing the HAVING Clause to a WHERE clause makes the query more efficient.
Yes, you can do this. Assuming that baan_view1b has all the records and notes has only some, change
FROM dbo.Baan_view1b INNER JOIN dbo.notes
to say
FROM dbo.Baan_view1b LEFT OUTER JOIN dbo.notes
INNER JOIN (or just plain JOIN) tells the database engine to take records from Baan_view1b, match them up with records in notes, and include a row in the output for every pair of records that match. As you have seen, it excludes records from Baan_view1b that don't have matches in the notes table.
LEFT OUTER JOIN instead tells the engine to take ALL the records from Bann_view1b (because it's on the left side of the JOIN keywords). Then, it will match up records from notes wherever it can. However, you are guaranteed a row in the output for every row in the left-hand table regardless of whether it can be matched.
If, as is usual, you asked for column values from both tables, the columns from the table on the right-hand side of the JOIN will have NULL values in the missing rows.
Change the inner join to a left outer join.
(Or a right outer join or a full outer join if you feel fancy.)
You need a outer join. This shows all records that have a matching key as well as the ones that don't. The inner join only shows records that have matching join keys.
Enjoy!
You need to do a Left Outer Join as other posters have already mentioned. More information can be found here.

Compare inner join and outer join SQL statements

What is the difference between an inner join and outer join? What's the precise meaning of these two kinds of joins?
Check out Jeff Atwood's excellent:
A Visual Explanation of SQL Joins
Marc
Wikipedia has a nice long article on the topic [here](http://en.wikipedia.org/wiki/Join_(SQL))
But basically :
Inner joins return results where there are rows that satisfy the where clause in ALL tables
Outer joins return results where there are rows that satisfy the where clause in at least one of the tables
You use INNER JOIN to return all rows from both tables where there is a match. ie. in the resulting table all the rows and columns will have values.
In OUTER JOIN the resulting table may have empty columns. Outer join may be either LEFT or RIGHT
LEFT OUTER JOIN returns all the rows from the first table, even if there are no matches in the second table.
RIGHT OUTER JOIN returns all the rows from the second table, even if there are no matches in the first table.
INNER JOIN returns rows that exist in both tables
OUTER JOIN returns all rows that exist in either table
Inner join only returns a joined row if the record appears in both table.
Outer join depending on direction will show all records from one table, joined to the data from them joined table where a corresponding row exists
Using mathematical Set,
Inner Join is A ^ B;
Outer Join is A - B.
So it is (+) is your A side in the query.
Assume an example schema with customers and order:
INNER JOIN: Retrieves customers with orders only.
LEFT OUTER JOIN: Retrieves all customers with or without orders.
RIGHT OUTER JOIN: Retrieves all orders with or without matching customer records.
For a slightly more detailed infos, see Inner and Outer Join SQL Statements