Reputation: 499
I have a table, Table, with two columns, PKColumn1 and Column2 in a SQL Server database. The table has a clustered index, clustered on PKColumn1.
If I use the following query against this table, I would expect the execution plan to show a Clustered Index Seek.
SELECT PKColumn1
FROM Table
WHERE PKColumn1 = 1
which it duly does.
If I use the following query against this table, I would also expect the execution plan to show a Clustered Index Seek.
DECLARE @PKColumn1 INT = 1;
SELECT PKColumn1
FROM Table
WHERE (PKColumn1 = @PKColumn1 OR @PKColumn1 IS NULL)
however I now see from the execution plan that the table was scanned.
Why is this?
Upvotes: 3
Views: 37
Reputation: 69524
In your second query the problem is in your where clause:
WHERE (PKColumn1 = @PKColumn1 OR @PKColumn1 IS NULL)
.
SQL Server does not do any short circuiting (like in c# ||), which means even if the expression @PKColumn1 IS NULL
evaluates to true, there is no guarantee that sql server will not evaluate the second expression PKColumn1 = @PKColumn1
.
Solution:
The best way to handle such optional parameters is to use dynamic SQL and built the query on the fly. Something like....
DECLARE @PKColumn1 INT = 1
,@Sql NVARCHAR(MAX);
SET @Sql = N' SELECT PKColumn1
FROM Table
WHERE 1 = 1 '
+ CASE WHEN @PKColumn1 IS NOT NULL THEN
N' AND PKColumn1 = @PKColumn1 ' ELSE N'' END
Exec sp_executesql @Sql
,N'@PKColumn1 INT'
,@PKColumn1
Using sp_executesql will cache the parameterised execution plan. Which is usually more of an issue when you have more than 2 optional parameters.
Upvotes: 3