SELECT COUNT('A') FROM [dbo].[OINV] T0
INNER JOIN [dbo].[OCRD] T2 ON T2.[CardCode] = T0.[CardCode]
WHERE T0.[CardCode] = (@P2) OR T2.[FatherCard] = (@P3)
you should remove include.Covering index
is not require from this query point of view.
I know you can't change query but throwing table structure along with data type and their null-ability help.
Why COUNT('A')
is not clear to me ?It should be Count(*)
or count(CardCode)
or which table count is require ?
one of index scan OCRD_FATHER
estimated rows is 2076000
another index scan is OINV_CUSTOMER] is 5175460
As we know in short when large number of rows are return or there is high cardianility estimate
then optimizer decide to scan than use index.
So you are getting index scan.
Suppose if any one of join was returning less number of rows,then Hash Match Join was very desirable and obvious.
How much rows Bitmap filter
return is not clear from your query plan .You can see from the tool tip on the arrow just after Bitmap filter
.
It must be returning far less rows than 2076000
In your current situation both are showing very very high Estimated rows,so Hash Match is not desirable.
This may be due to Outdated Statistics.
May be Bitmap filter is returning far less number of rows then Hash Match Join is obvious.
Bitmap filter : The primary role of the Bitmap is to speed up parallel plans by doing
semijoin reduction early on in the query, before rows are passed
through the Parallelism operator.
Since you can't rewrite query,you can't use Hint or any other way.only thing you can do is probe
XML plan reveal more thing than graphical plan in your case here.
XML plan reveal that there is CONVERT_IMPLICIT(int,[globalagg1006],0)
Due to part of predicate is under Residual Probe
:Residual probe happen because of inequality operator or implicit conversion
between data type.Residual probe predicate are filter much later.Residual predicate are executed after main predicate.Residual predicate may not be present in the tool tip,but it is present in xml plan
or property window
(hit F4).
This is also the reason that query is very slow.
T0.[CardCode] = (@P2) OR T2.[FatherCard] = (@P3)
Here ensure that @P2 data type is similar to T0.[CardCode].And @P3 data type should be similar to T2.[FatherCard].
Similarly T2.[CardCode] = T0.[CardCode]
data type should be similar.
Update 1
As per @Martin sample data,
query 1,
declare @P2 nvarchar(50)
declare @P3 nvarchar(50)
SELECT COUNT('A') FROM [dbo].[OINV] T0
INNER JOIN [dbo].[OCRD] T2 ON T2.[CardCode] = T0.[CardCode]
WHERE T0.[CardCode] = (@P2) OR T2.[FatherCard] = (@P3)
It takes around 17 sec in.
If change to T2[CardCode] = (@P2)
then it take less than 1 sec.Also Plan change from index scan to seek.
One of the reason for such drastic change is data type.
Now query 2, i change data type of parameter
declare @P2 varchar(50)
declare @P3 varchar(50)
SELECT COUNT('A') FROM [dbo].[OINV] T0
INNER JOIN [dbo].[OCRD] T2 ON T2.[CardCode] = T0.[CardCode]
WHERE T0.[CardCode] = (@P2) OR T2.[FatherCard] = (@P3)
It take 6 second to execute.In real data it will take more time if it produce result.
My execution plan are almost identical to that of @Zac .I am getting
table scan,3 parallelism,bitMap Create,Hash Join with predicate residual and Hash Probe residual.
If I change my where condition to T2.[CardCode] = (@P2)
It take 1 second and there some change in query plan.
Hash join change to Nested T2 is index seek.T0 is still table scan and produces parallelism.
Therefore my answer above "Update" about data type,residual etc are correct.
And this what happening in case of @Zac.