Case statement in JOIN slows the performance of the query - sql

Case statement in JOIN slows the performance of the query, is there any way to improve to make the query performance faster
SELECT A.TestColumn
FROM A with(nolock)
INNER JOIN B with(nolock)
ON A.ID = B.ID
and A.InvoiceNo = CASE WHEN B.InvoiceType=2
THEN B.MainInvoiceNo
ELSE B.InvoiceNo END

Try :
1) with the valued CASE
SELECT A.TestColumn
FROM A
INNER JOIN B
ON A.ID = B.ID and
A.InvoiceNo = CASE B.InvoiceType
WHEN 2 THEN B.MainInvoiceNo
ELSE B.InvoiceNo
END
2) with a UNION ALL
SELECT A.TestColumn
FROM A
INNER JOIN B
ON A.ID = B.ID and
A.InvoiceNo = B.MainInvoiceNo
WHERE B.InvoiceType=2
UNION ALL
SELECT A.TestColumn
FROM A
INNER JOIN B
ON A.ID = B.ID and
A.InvoiceNo = B.InvoiceNo
WHERE B.InvoiceType<>2

probably using union will be the best performant :
SELECT A.TestColumn
FROM A
INNER JOIN B
ON A.ID = B.ID
and B.InvoiceType=2
and A.InvoiceNo = B.MainInvoiceNo
union all
SELECT A.TestColumn
FROM A with(nolock)
INNER JOIN B with(nolock)
ON A.ID = B.ID
and B.InvoiceType <> 2
and A.InvoiceNo = B.InvoiceNo
but also you can try using OR :
SELECT A.TestColumn
FROM A with(nolock)
INNER JOIN B with(nolock)
ON A.ID = B.ID
and
(
(B.InvoiceType= 2 and A.InvoiceNo = B.MainInvoiceNo)
or
(B.InvoiceType<> 2 and A.InvoiceNo = B.InvoiceNo)
)

I would suggest two LEFT JOINs and a WHERE
SELECT A.TestColumn
FROM A LEFT JOIN
B B1
ON A.ID = B1.ID AND
B1.InvoiceType = 2
A.InvoiceNo = B.MainInvoiceNo LEFT JOIN
B B2
ON A.ID = B2.ID AND
A.InvoiceNo = B2.InvoiceNo
WHERE B1.ID IS NOT NULL OR B2.ID IS NOT NULL;
SQL Server should find it much easier to implement an efficient execution plan. I would recommend indexes on B(ID, MainInvoiceNo, InvoiceType) and B(ID, InvoiceNo).

Using a case expression as part of the join criteria will be forcing SQL server to scan your index (probably a table scan if id is a clustered index).
Depending on the cardinality of your data and expected rows for each join condition, using two separate queries with the results unioned can yield better performance.
This is because for each individual query the optimiser can likely utilise an index seek resulting in far few logical reads overall.
Also since you are only returning data from A you could try using exists
Select A.TestColumn
from A
where exists (
select * from B where B.Id = A.Id and B.InvoiceType=2 and A.InvoiceNo=B.MainInvoiceNo
)
or exists (
select * from B where B.Id=A.Id and B.InvoiceNo = A.InvoiceNo
)

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.

Access SQL - Joins on multple tables

I have come across a Join Error in Access SQL, when using multiple "ON" criteria's. I am unable to perform an ON clause on 2 different tables, for example:
select *
from
(((A
left join B on a.id = b.id)
left join c on c.id = b.id)
left join D
on (d.id = b.id) and (d.id = a.id)
That final join statement causes an error because I link table D on table B first, and then link Table D on Table A. If I choose to instead link table D on Table B again, then it resolves. However, I need to join it this way due to the certain data I need to link Table D on from both tables.
How can I more efficiently structure my query to achieve my results?
you may try this select * from A left join B on a.id = b.id left join c on c.id = b.id left join D on d.id = b.id and d.id = a.id
It's somewhat difficult to tell what you're trying to do exactly, but most likely, this is what you want:
select *
from
(((A
left join B on a.id = b.id)
left join C on c.id = b.id)
left join D on d.id = a.id)
Since you're trying a LEFT JOIN, there is no reason to link multiple ids to eachother.

JOIN on column only if NOT NULL

I'm in the process of re-writing an old SQL query and have troubles making sense out of it. It contains several conditions of the form
SELECT ...
FROM a, b, c
WHERE
c.id = ...
AND (
a.x_id IS NULL
OR a.x_id = c.x_id
)
AND b.id = a.b_id (+)
Can this query be rewritten using proper JOIN syntax? Is it equivalent to the following or will it produce different results under certain circumstances?
SELECT ...
FROM b
LEFT JOIN a
ON b.id = a.b_id
LEFT JOIN c
ON a.x_id = c.x_id
WHERE c.id = ...
The original query is 100 lines long and spans 5 tables, plus several joins over "virtual tables" (i.e. where conditions of the form x.z_id = y.z_id), which makes it hard to break down into more manageable bits or debug.
if you want same result as you have in first query - you must make left join only with table a, like this :
SELECT ...
FROM b, c
LEFT JOIN a
ON b.id = a.b_id and b.id = a.b_id
WHERE
c.id = ... b.c_id
or if you want the same style with all tables, you can use inner join with table b, like this :
SELECT ...
FROM c
INNER JOIN b
on b.c_id = c.id
LEFT JOIN a
ON b.id = a.b_id
WHERE
c.id = ...
in my both query we select data from table b where column is not null

How make sql query "not in" more simply use only "join"?

Have query:
select a.id from selzde.elorder a
inner join selzde.elorder b on a.name = b.name
and a.workname = b.workname
and b.id = needId
where a.id not in (select id_elorder from selzde.drugselorder)
how make sql query not in more simply use only join?
select a.id from selzde.elorder a
inner join selzde.elorder b on a.name = b.name
and a.workname = b.workname
and b.id = needId
left outer join selzde.drugselorder d on a.id = d.id_elorder
where d.id_elorder is null
Left join to drugselorder, on id_elorder = a.id, then choose the ones with no match (i.e. where drugselorder is null)... I wouldn't say it was any more "simple", though.
One solution would be to use MINUS operator rather than not in as follows:
select a.id
from selzde.elorder a
inner join selzde.elorder b
on a.name = b.name
and a.workname = b.workname
and b.id = needId
MINUS
select id_elorder
from selzde.drugselorder
;
Hope this helps.
Regards,
Roger

Simulate a left join without using "left join"

I need to simulate the left join effect without using the "left join" key.
I have two tables, A and B, both with id and name columns. I would like to select all the dbids on both tables, where the name in A equals the name in B.
I use this to make a synchronization, so at the beginning B is empty (so I will have couples with id from A with a value and id from B is null). Later I will have a mix of couples with value - value and value - null.
Normally it would be:
SELECT A.id, B.id
FROM A left join B
ON A.name = B.name
The problem is that I can't use the left join and wanted to know if/how it is possible to do the same thing.
you can use this approach, but you must be sure that the inner select only returns one row.
SELECT A.id,
(select B.id from B where A.name = B.name) as B_ID
FROM A
Just reverse the tables and use a right join instead.
SELECT A.id,
B.id
FROM B
RIGHT JOIN A
ON A.name = B.name
I'm not familiar with java/jpa. Using pure SQL, here's one approach:
SELECT A.id AS A_id, B.id AS B_id
FROM A INNER JOIN B
ON A.name = B.name
UNION
SELECT id AS A_id, NULL AS B_id
FROM A
WHERE name NOT IN ( SELECT name FROM B );
In SQL Server, for example, You can use the *= operator to make a left join:
select A.id, B.id
from A, B
where A.name *= B.name
Other databases might have a slightly different syntax, if such an operator exists at all.
This is the old syntax, used before the join keyword was introduced. You should of course use the join keyword instead if possible. The old syntax might not even work in newer versions of the database.
I can only think of two ways that haven't been given so far. My last three ideas have already been given (boohoo) but I put them here for posterity. I DID think of them without cheating. :-p
Calculate whether B has a match, then provide an extra UNIONed row for the B set to supply the NULL when there is no match.
SELECT A.Id, A.Something, B.Id, B.Whatever, B.SomethingElse
FROM
(
SELECT
A.*,
CASE
WHEN EXISTS (SELECT * FROM B WHERE A.Id = B.Id) THEN 1
ELSE 0
END Which
FROM A
) A
INNER JOIN (
SELECT 1 Which, B.* FROM B
UNION ALL SELECT 0, B* FROM B WHERE 1 = 0
) B ON A.Which = B.Which
AND (
A.Which = 0
OR (
A.Which = 1
AND A.Id = b.Id
)
)
A slightly different take on that same query:
SELECT A.Id, B.Id
FROM
(
SELECT
A.*,
CASE
WHEN EXISTS (SELECT * FROM B WHERE A.Id = B.Id) THEN A.Id
ELSE -1 // a value that does not exist in B
END PseudoId
FROM A
) A
INNER JOIN (
SELECT B.Id PseudoId, B.Id FROM B
UNION ALL SELECT -1, NULL
) B ON A.Which = B.Which
AND A.PseudoId = B.PseudoId
Only for SQL Server specifically. I know, it's really a left join, but it doesn't SAY LEFT in there!
SELECT A.Id, B.Id
FROM
A
OUTER APPLY (
SELECT *
FROM B
WHERE A.Id = B.Id
) B
Get the inner join then UNION the outer join:
SELECT A.Id, B.Id
FROM
A
INNER JOIN B ON A.name = B.name
UNION ALL
SELECT A.Id, NULL
FROM A
WHERE NOT EXISTS (
SELECT *
FROM B
WHERE A.Id = B.Id
)
Use RIGHT JOIN. That's not a LEFT JOIN!
SELECT A.Id, B.Id
FROM
B
RIGHT JOIN A ON B.name = A.name
Just select the B value in a subquery expression (let's hope there's only one B per A). Multiple columns from B can be their own expressions (YUCKO!):
SELECT A.Id, (SELECT TOP 1 B.Id FROM B WHERE A.Id = B.Id) Bid
FROM A
Anyone using Oracle may need some FROM DUAL clauses in any SELECTs that have no FROM.
You could use subqueries, something like:
select a.id
, nvl((select b.id from b where b.name = a.name), "") as bId
from a
you can use oracle + operator for left join :-
SELECT A.id, B.id
FROM A , B
ON A.name = B.name (+)
Find link :-
Oracle "(+)" Operator
SELECT A.id, B.id
FROM A full outer join B
ON A.name = B.name
where A.name is not null
I'm not sure if you just can't use a LEFT JOIN or if you're restricted from using any JOINS at all. But as far as I understand your requirements, an INNER JOIN should work:
SELECT A.id, B.id
FROM A
INNER JOIN B ON A.name = B.name
Simulating left join using pure simple sql:
SELECT A.name
FROM A
where (select count(B.name) from B where A.id = B.id)<1;
In left join there are no lines in B referring A so 0 names in B will refer to the lines in A that dont have a match
+ or A.id = B.id in where clause to simulate the inner join