SQL where clause for left outer join - sql

I have a problem with a view I want to create. I have two tables joined in a left outer join, say tableA and tableB, where tableB is left outer joined.
I want to select only those rows from table B where state equals 4, so I add WHERE state = 4 to my query. Now the result set is trimmed quite a bit because all rows without a matching row in tableB are removed from the result (since state isn't 4 for those rows). I also tried WHERE state = 4 OR state IS NULL, doesn't work either (since state technically isn't NULL when there is no state).
So what I need is a WHERE statement which is only evaluated when there actually is a row, does such a thing exist?
If not I see two options: join (SELECT * FROM tableB WHERE state = 4) instead of table B, or create a view with the same WHERE statement and join that instead. What's the best option performance wise?
This is SQL Server 2008 R2 by the way.

You put the conditions in the on clause. Example:
select a.this, b.that
from TableA a
left join TableB b on b.id = a.id and b.State = 4

You can add state = 4 to the join condition.
select *
from T1
left outer join T2
on T1.T1ID = T2.T1ID and
T2.state = 4

Even easier than a subquery is expanding the on clause, like;
select *
from TableA a
left join
TableB b
on a.b_id = b.id
and b.state = 4
All rows from TableA will appear, and only those from TableB with state 4.
SQL Server will probably execute the view, expanded on, and subquery in exactly the same way. So performance wise, there should be little difference.

Alternative approach: (1) inner join to table B where state equals 4, (2) antijoin to table B to find rows that don't exist, (3) union the results:
SELECT A1.ID, A1.colA, B1.ColB
FROM tableA AS A1
INNER JOIN tableB AS B1
ON A1.ID = B1.ID
AND B1.state = 4
UNION
SELECT A1.ID, A1.colA, '{{MISSING}}' AS ColB
FROM tableA AS A1
WHERE NOT EXISTS (
SELECT *
FROM tableB AS B1
WHERE A1.ID = B1.ID
);
Alternatively:
SELECT A1.ID, A1.colA, B1.ColB
FROM tableA AS A1
JOIN tableB AS B1
ON A1.ID = B1.ID
AND B1.state = 4
UNION
SELECT ID, colA, '{{NA}}' AS ColB
FROM tableA
WHERE ID IN (
SELECT ID
FROM tableA
EXCEPT
SELECT ID
FROM tableB
);

Related

SQL antijoin with multiple keys

I'd like to implement an antijoin on two table but using two keys so that the result is all rows in Table A that do not contain the combinations of [key_1, key_2] found in Table B. How can I write this query in SQL?
If you want an anti-left join, the logic is:
select a.*
from tablea a
left join tableb b on b.key_1 = a.key_1 and b.key_2 = a.key_2
where b.key_1 is null
As for me, I like to implement such logic with not exists, because I find that it is more expressive about the intent:
select a.*
from tablea a
where not exists (
select 1 from tableb b where b.key_1 = a.key_1 and b.key_2 = a.key_2
)
The not exists query would take advantage of an index on tableb(key_1, key_2).
select a.*
from table_a a
left anti join table_b b on a.key_1 = b.key_1 and a.key_2 = b.key_2;

Cancel join condition based on column value

I am stuck into a situation where I need to perform conditional join. The brief summary could be, I have 2 tables TableA and TableB.
TableA has columns A1,A2,A3,A4,A5,Condition1,Condition2
similarly TableB has columns B1,B2,Condition1,Condition2
And I need to perform join on TableA.Condition1 = TableB.Condition1 and condition join on TableA.Condition2 = TableB.Condition2, condition being TableA.Condition2 should not be null for any record first join.
In other words, if I some any record with TableA.Condition1 = TableB.Condition1 matched and at the same time TableA.Condition2 is not null for any of them then perform second join, otherwise don't perform second join.
Query could be like
SELECT A.* FROM TableA A
INNER JOIN TableB B
ON A.Condition1 = B.Condition1 -- This must be perform
AND WHEN A.Condition2 IS NULL THEN
1 = 1 -- Assuming no join here
ELSE
A.Condition2 = B.Condition2 -- perform join
END
You are only selecting from TableA, so how about using exists instead?
SELECT A.*
FROM TableA A
WHERE (A.Condition2 IS NULL AND
EXISTS (SELECT 1 FROM TableB b WHERE A.Condition1 = B.Condition1)
) AND
(A.Condition2 IS NOT NULL AND
EXISTS (SELECT 1 FROM TableB b WHERE A.Condition1 = B.Condition1 AND A.Condition2 = B.Condition2)
);
Or, if you want a JOIN:
SELECT A.*
FROM TableA A JOIN
TableB B
ON A.Condition1 = B.Condition1 AND
(A.Condition2 IS NOT NULL OR A.Condition2 = B.Condition2);
Try this may help you
SELECT A.*
FROM TableA A
INNER JOIN TableB B ON A.Condition1 = B.Condition1 AND
((A.Condition2 IS NOT NULL AND A.Condition2 = B.Condition2)
OR (A.Condition2 IS NULL) )
hard to understand your question and even harder to understand the purpose
SELECT A.*
-- join table1 and table2 on Condition1
FROM TableA A
JOIN TableB B ON A.Condition1 = B.Condition1
-- if max condition2 is null then there is nothing but nulls
JOIN ( SELECT MAX(Condition2) Condition2 FROM TableA A2 ) A2
-- in that case every row resulting from join1 goes
ON A2.Condition2 IS NULL
-- otherwise use condition2 but replace nulls with some placeholder
-- or maybe you have either all null or no nulls
OR COALESCE (A.Condition2,'null') = COALESCE (B.Condition2,'null')
;

Optimizing SQL Query - Joining 4 tables

I am trying to join 4 tables. Currently I've achieved it by doing this.
SELECT columns
FROM tableA
LEFT OUTER JOIN tableB ON tableB.address_id = tableA.address_id
INNER JOIN tableC ON tableC.company_id = tableA.company_id AND tableC.client_id = ?
UNION
SELECT columns
FROM tableA
LEFT OUTER JOIN tableB ON tableB.address_id = tableA.gaddress_id
INNER JOIN tableD ON tableD.company_id = tableA.company_id AND tableD.branch_id = ?
The structure of tableC and tableD is very similar. Let's say that tableC contains data for clients. And tableD contains data for client's branch. tableA are companies and tableB are addresses My goal is to get data from tableA that are joined to table B (All companies that has addresses) and all the data from tableD and also from tableC.
This wroks nice, but I am afraid that is would be very slow.
I think you can trick it like this:
First UNION between C,D and only the join to the rest of the query, it should improve the query significantly :
SELECT columns
FROM TableA
LEFT OUTER JOIN tableB ON tableB.address_id = tableA.address_id
INNER JOIN(SELECT Columns,'1' as ind_where FROM tableC
UNION ALL
SELECT Columns,'2' FROM TableD) joined_Table
ON (joined_Table.company_id = tableA.company_id AND joined_Table.New_Col_ID= ?)
The New_Col_ID -> just select both branch_id and client_id in the same column and alias it as New_Col_ID or what ever
In addition you can index the tables(if not exists yet) :
TableA(address_id,company_id)
TableB(address_id)
TableC(company_id,client_id)
TableD(company_id,branch_id)
Why should that be slow? You select client adresses and branch addresses and show the complete result. That seems straight-forward.
You join on IDs and this should be fast (as there should be indexes available accordingly). You may want to introduce composite indexes on
create index idx_c on tableC(client_id, company_id)
and
create index idx_d on tableD(branch_id, company_id)
However: UNION is a lot of work for the DBMS, because it has to look for and eliminate duplicates. Can there even be any? Otherwise use UNION ALL.
Try CTE so that you don't have to go through TableA and TableB twice for the union.
; WITH TempTable (Column1, Column2, ...)
AS ( SELECT columns
FROM tableA
LEFT OUTER JOIN tableB
ON tableB.address_id = tableA.gaddress_id
)
SELECT Columns
FROM TempTable
INNER JOIN tableC
ON tableC.company_id = tableA.company_id AND tableC.client_id = ?
UNION
SELECT Columns
FROM TempTable
INNER JOIN tableD ON tableD.company_id = tableA.company_id AND tableD.branch_id = ?

SQL - not sure how to join tables

I'm trying to join two tables like this:
Table A
ID Value1
1 A
2 B
3 C
Table B
ID Value2
1 A
3 B
4 C
Result should be:
ID Value1 Value2
1 A A
2 B null
3 C B
4 null C
I.e. join Table A to Table B on ID. If ID doesn't exist in Table A, add the ID from Table B.
The closest I've come is:
SELECT
a.ID, a.Value1, b.Value2
FROM
TableA a
OUTER JOIN
TableB b ON a.ID = b.ID
That gives me the new rows from TableB, but the ID is null.
How can I accomplish this?
You are very close, you just need a little push in the right direction:
SELECT COALESCE(a.ID, B.ID) As ID, a.Value1, b.Value2
FROM TableA a
FULL OUTER JOIN TableB b ON a.ID=b.ID
The COALESCE function returns the first parameter it gets that is not null. since this is a full outer join, a.id will be null on one row and b.id would be null on a different row.
Try this:
SELECT *
FROM TableA A
FULL OUTER JOIN TableB B
ON A.ID = B.ID;
Just a note: you should not name your tables in SQL with spaces in them.
Remember the basic for joining different tables
SELECT column_name(s)
FROM table1
FULL OUTER JOIN table2
ON table1.column_name=table2.column_name;
For your case:
SELECT a.value1, b.value2
FROM TableA a
FULL OUTER JOIN TableB b ON a.ID=b.ID
remember full outer join
The FULL OUTER JOIN keyword returns all rows from the table (tableA) and from the table (tableB) and the FULL OUTER JOIN keyword combines the result of both LEFT and RIGHT joins.

Is it possible to use IF or CASE in sql FROM statement

I have a long stored procedure and I would like to make a slight modification to the procedure without having to create a new one(for maintenance purposes).
Is it possible to use a IF or CASE in the FROM statement of the select statement to join other tables?
Like this:
from tableA a
join tableB b a.indexed = c.indexed
IF #Param='Y'
BEGIN
join tableC c a.indexed = c.indexed
END
It didn't seem to work for me. But I am wondering if this is even possible and/or if this even makes sense to do.
Thanks.
No, it is not possible. You can only accomplish this through the use of dynamic SQL.
The Curse and Blessings of Dynamic SQL
An Intro to Dynamic SQL
I would not advise using Dynamic SQL, there are most likely better ways to perform this operation but you would have to provide more info.
You can achieve something like it if you have a left outer join
Consider
declare #param bit = 1
select a.*, b.*, c.* from a
inner join b on a.id = b.a_id
left outer join c on b.id = c.b_id and #param = 1
This will return all columns from a, b, c.
Now try with
declare #param bit = 0
This will return all columns from a and b, and nulls for columns of c.
It won't work if both joins are inner.
No this is not possible. Your best bet would probably be to select from both tables and only include the data your care about. If you provide an example of what you are trying to do I can provide a better answer.
Attempt at an example:
SELECT t1.id, COALESCE(t2.name, t3.name)
FROM Table1 as t1
LEFT JOIN Table2 as t2
ON t1.id = t2.id
LEFT JOIN Table2 as t3
ON t1.id = t3.id
While what you proposed is not possible, you can play with your where conditions:
from tableA a
inner join tableB b ON a.indexed = c.indexed
left join tableC c ON a.indexed = c.indexed AND 1 = CASE #Param WHEN 'Y' THEN 1 ELSE 0 END
More performant would be to just doing a big
IF #Param='Y' THEN
from tableA a
inner join tableB b ON a.indexed = c.indexed
ELSE
from tableA a
inner join tableB b ON a.indexed = c.indexed
left join tableC c ON a.indexed = c.indexed
You haven't revealed you SELECT clause. The essence of what you want is as follows:
SELECT indexed
FROM tableA
INTERSECT
SELECT indexed
FROM tableB
INTERSECT
SELECT indexed
FROM tableC
WHERE #Param = 'Y'
Then use this table expression as dictated by your SELECT clause e.g. say you only want to project tableA:
WITH T
AS
(
SELECT indexed
FROM tableA
INTERSECT
SELECT indexed
FROM tableB
INTERSECT
SELECT indexed
FROM tableC
WHERE #Param = 'Y'
)
SELECT *
FROM tableA
WHERE indexed IN ( SELECT indexed FROM T );