Using Join based on condition - sql

Can anyone please explain me how can we use join on the basis of condition.
Lets say i am filtering data on the basis of a condition now my concern is if a particular BIT type parameters value is 1 then the data set include one more join else return same as earlier.
Here is three tables A,B,C
now i want to make a proc which has the #bool bit parameter
if #bool=0
then
select A.* from A
inner join B on B.id=A.id
and if #bool=1
then
select A.* from A
INNER JOIN B on B.id=A.id
inner join C on C.id=A.id
Thanks In Advance.

What you have will work (certainly in a SPROC in MS SQL Server anyway) with minor mods.
if #bool=0 then
select A.* from A
inner join B on B.id=A.id
else if #bool=1 then -- Or just else if #boll is limited to [0,1]
select A.* from A
INNER JOIN B on B.id=A.id
inner join C on C.id=A.id
However, the caveat is that SQL parameter sniffing will cache a plan for the first path it goes down, which won't necessarily be optimal for other paths through your code.
Also, if you do take this 'multiple alternative query' approach to your procs, it is generally a good idea to ensure that the column names and types returned are identitical in all cases (Your query is fine because it is A.*).
Edit
Assuming that you are using SQL Server, an alternative is to use dynamic sql:
DECLARE #sql NVARCHAR(MAX)
SET #sql = N'select A.* from A
inner join B on B.id=A.id'
IF #bool = 1
SET #sql = #sql + N' inner join C on C.id=A.id'
sp_executesql #sql
If you need to add filters etc, have a look at this post: Add WHERE clauses to SQL dynamically / programmatically

select A.* from A
inner join B on B.id = A.id
left outer join C on C.id = A.id and #bool = 1
where (#bool = 1 and C.id is not null) or #bool = 0
The #bool = 1 "activates" the left outer join, so to speak, and turns it, in effect, into an inner join by applying it in the WHERE clause, too. If #bool = 0 then the left outer join returns nothing from C and removes the WHERE restriction.

Try the following query
SELECT A.*
FROM A
INNER JOIN B on B.id=A.id
INNER JOIN C on C.id=A.id and #bool=1

You do it using a union:
SELECT A.*
FROM A
INNER JOIN B on B.id=A.id
WHERE bool = 0
UNION ALL
SELECT A.*
FROM A
INNER JOIN B on B.id=A.id
INNER JOIN C on C.id=A.id
WHERE bool = 1
I'm assuming that bool is stored in table A or B.

Related

Select and update on the rows in a table by using joins multiple table in SQL Server

I am trying to update 1.2 million rows in a table that had data inserted kind of incorrectly via legacy application. I am not very good at writing efficient SQL queries as I am experiencing these sort of larger set of data for the first time.
I have written query as below and it's taking a very long time to run this query. I have commented out my update logic in the statement.
SELECT T1.Old_id,
T1. Report_id,
T2.New_id /* update a set file_id = T2.new_id*/
FROM
(SELECT A.File_id AS Old_id,
A.Id AS Report_id,
A.User_id AS USER
FROM A
INNER JOIN B ON A.Id = B.A_id
INNER JOIN C ON B.Id = C. B_id
INNER JOIN D ON C.Id = D.C_id
INNER JOIN E ON D.Id = E.D_id
WHERE E.Name = 'student_report') AS T1
LEFT JOIN
(SELECT Max(C.Report_id) AS New_id,
C.Created_by AS User_id
FROM C
INNER JOIN D ON C.Id = D.C_id
INNER JOIN E ON D.Id = E.D_id
WHERE E.Name = 'teacher_report'
GROUP BY C.Created_by) ON T1.User_id = T2.User_id /* where a.id = T1.report_id*/
I need to update the file_id in table a by the report_id of c. With a small set of data, the select query works fine and gives the result as intended. But on the server where it has 1.2 million rows, it takes extremely long time.
Is there a way we could put those two sub-queries into one and make it work for 'update' as well? Because, update also fails as it has 'group by' on the second sub-query.
Main problem is using Subquery in join condition.
Second problem,when same resultset is to be use multiple time then you should put common resultset in CTE or #temp table.
create table #temp(B_id int,cTeport_id int,cUserID int,EName varchar(100))
insert into #temp
select B_id,C.Report_id,C.Created_by,E.Name
INNER JOIN C ON B.Id = C. B_id
INNER JOIN D ON C.Id = D.C_id
INNER JOIN E ON D.Id = E.D_id
WHERE E.Name in( 'teacher_report','student_report')
;With CTE as
(
SELECT Max(C.Report_id) AS New_id,
C.Created_by AS User_id
FROM #temp c
WHERE c.Name = 'teacher_report'
GROUP BY C.Created_by
)
SELECT T1.Old_id,
T1. Report_id,
T2.New_id /* update a set file_id = T2.new_id*/
FROM
(SELECT A.File_id AS Old_id,
A.Id AS Report_id,
A.User_id AS USER
FROM A
INNER JOIN B ON A.Id = B.A_id
INNER JOIN #temp t ON B.Id = t. B_id
WHERE t.Name = 'student_report'
and exists(select 1 from cte t1 T1.User_id = T.User_id)
My script is not Tested so you can fix any minor bug if any.
In Temp table carefully define all columns which is require for this query along with their datatype.
Please analyze the Query cost by using Execution Plan. Check the table which is making delay then check proper Indexing used for that particular table or not.

"On" left join order

I've read through 20+ posts with a similar title, but failed to find an answer, so apologies in advance if one is available.
I have always believed that
select * FROM A LEFT JOIN B on ON A.ID = B.ID
was equivalent to
select * FROM A LEFT JOIN B on ON B.ID = A.ID
but was told today that "since you have a left join, you must have it as A = B, because flipped it will act as an inner join.
Any truth to this?
Whoever told you that does not understand how JOINs and join conditions work. He/She is completely wrong.
The order of the tables matters for a left join. a left join b is different than b left join a, but the order of the join condition is meaningless.
A.ID = B.ID is the condition on which the tables are joined and returns TRUE or FALSE.
Since equality(=) is commutative, the order of the operands does not affect the result.
They are completely incorrect and it is trivial to prove.
DECLARE #A TABLE (ID INT)
DECLARE #B TABLE (ID INT)
INSERT INTO #A(ID) SELECT 1
INSERT INTO #A(ID) SELECT 2
INSERT INTO #B(ID) SELECT 1
SELECT *
FROM #A a
LEFT JOIN #B b ON a.ID=b.ID
SELECT *
FROM #A a
LEFT JOIN #B b ON b.ID=a.ID
The order of the tables matter (A Left JOIN B versus B LEFT JOIN A), the order of the join condition group matter if an OR is used (A=B OR A IS NULL AND A IS NOT NULL - always use parentheses with OR), but within a condition group(a.ID=b.ID for example) it doesn't matter.

When to open and close brackets surrounding joins in MS Access SQL

I want to understand when to open and close brackets when representing joins in MS Access queries as I am developing a query builder using C++ for MS Access queries so that I can apply the same code to generate similar queries.
SELECT
MasterTool.Name, Toolsets.SlaveToolID, Tools.MachineID
FROM
Tools AS MasterTool
LEFT JOIN
(
Toolsets LEFT JOIN Tools ON Toolsets.SlaveToolID = Tools.ID
)
ON MasterTool.ID = Toolsets.MasterToolID
Edit:
#LeeMac as per your explaination when i modified the query which i presented earlier to this
SELECT Tools.Name, Toolsets.SlaveToolID, Tools.MachineID FROM (Tools
LEFT JOIN Toolsets ON Toolsets.SlaveToolID = Tools.ID )
LEFT JOIN Tools ON Toolsets.MasterToolID = Tools.ID
i am getting error Join Expression Not Supported is there is any simple way to write the above query.
Essentially, when an MS Access query references more than two tables, every successive join between a pair of tables should be nested within parentheses.
For example, a query with two tables requires no parentheses:
select *
from a inner join b on a.id = b.id
The addition of a third joined table necessitates parentheses surrounding the original join in order to distinguish it from the additional join:
select *
from
(
a inner join b on a.id = b.id
)
inner join c on a.id = c.id
Every successive addition of a table will then cause the existing joins to be nested within another level of parentheses:
select *
from
(
(
a inner join b on a.id = b.id
)
inner join c on a.id = c.id
)
inner join d on a.id = d.id
Hence, in general:
select *
from
(
(
(
(
table1 [inner/left/right] join table2 on [conditions]
)
[inner/left/right] join table3 on [conditions]
)
[inner/left/right] join table4 on [conditions]
)
...
)
[inner/left/right] join tableN on [conditions]
There is a subtlety where LEFT/RIGHT joins are concerned, in that the order of nesting must maintain the direction of the join, for example:
select *
from
(
c left join b on c.id = b.id
)
left join a on a.id = b.id
Could be permuted to:
select *
from
c left join
(
b left join a on b.id = a.id
)
on c.id = b.id

How to use oracle outer join with a filter where clause

If i write a sql:
select *
from a,b
where a.id=b.id(+)
and b.val="test"
and i want all records from a where corresponding record in b does not exist or it exists with val="test", is this the correct query?
You're much better off using the ANSI syntax
SELECT *
FROM a
LEFT OUTER JOIN b ON( a.id = b.id and
b.val = 'test' )
You can do the same thing using Oracle's syntax as well but it gets a bit hinkey
SELECT *
FROM a,
b
WHERE a.id = b.id(+)
AND b.val(+) = 'test'
Note that in both cases, I'm ignoring the c table since you don't specify a join condition. And I'm assuming that you don't really want to join A to B and then generate a Cartesian product with C.
Move the condition into the JOIN clause and use the ANSI standard join pattern.
SELECT NameYourFields,...
FROM A
LEFT OUTER JOIN B
ON A.ID = B.ID
AND B.VAL = 'test'
INNER JOIN C
ON ...
A LEFT OUTER JOIN is one of the JOIN operations that allow you to specify a join clause. It preserves the unmatched rows from the first (left) table, joining them with a NULL row in the shape of the second (right) table.
So you can do as follows :
SELECT
FROM a LEFT OUTER JOIN b
ON a.id = b.id
--Note that you have used double quote "test" which is not used for varchar in SQL you should use single quote 'test'
AND b.val = 'test';
SELECT * FROM abc a, xyz b
WHERE a.id = b.id
AND b.val = 'test'

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 );