Reputation: 681
I have two tables a, b, primary key is their index.
Requirement:
If @filter
is empty, select all records of a, b, else split @filter
by any specific separator, find records whose b.PKey
are in the filter.
Current Implementation:
declare @filter nvarchar(max)= ''
SELECT *
FROM a
JOIN b ON a.PKey = b.aPKey
AND (@filter = '' OR b.PKey IN (SELECT item FROM splitFunction(@filter))
I found the last statement and (@filter = '' or b.PKey in (select item from splitFunction(@filter))
will always cause a table scan on table b, only if I remove @filter=''
, it will change to index seek.
Is there any way can implement my requirement and does not harm the performance?
Upvotes: 3
Views: 1852
Reputation: 1304
For constants, the optimizer can work out the best plan based on statistics for the given values.
When you use variable you are forcing parameterization and the plan will be designed to be reusable for a wide range of values. So Optimizer uses scan and not seek. To overcome this problem stop using local variables in the query and use this as a parameter in stored procedure
create procedure p1
@filter = ''
as
begin
SELECT *
FROM a
JOIN b ON a.PKey = b.aPKey
AND (@filter = '' OR b.PKey IN (SELECT item FROM splitFunction(@filter))
option (recompile)
end
This will give you the correct plan as in the stored procedure I have converted your local variable into parameter.
Upvotes: 1
Reputation: 950
Specifically for this case, you can also try this:
DECLARE @filter nvarchar(max)= ''
IF @filter = ''
BEGIN
SELECT *
FROM a
JOIN b ON a.PKey = b.aPKey
END
ELSE
BEGIN
SELECT *
FROM a
JOIN b ON a.PKey = b.aPKey
WHERE b.PKey IN (SELECT item FROM splitFunction(@filter))
END
Upvotes: 1