2

i've got a procedure that i want to improve that uses this where clause:

WHERE   p.PlatformId = ISNULL(@PlatformId, p.PlatformId)
and p.PayeeStsId     = 1
and p.PlatformPayeeId   = ISNULL(@platformPayeeId, p.PlatformPayeeId)
and pba.PayeeBankStsId=1
and a.Gcid = ISNULL(@Gcid, a.Gcid)

the ISNULL is causing me problems with using the index how can i avoid using this format of ISNULL?

p.PlatformId = ISNULL(@PlatformAccountId, p.PlatformId)

this format is causing the execution plan to do index scan.

6
  • How do you know ISNULL() is your root problem?...show the full query and actual execution plan when you have a performance question. The only thing that can be advised otherwise is just re-write your query to not use ISNULL() with a series of ORs or UNION ALLs for each case your ISNULL() code currently covers.
    – J.D.
    Commented Jan 4 at 22:34
  • 6
    Unless the query is executed many times per second, try WHERE (p.PlatformId = @PlatformId OR @PlatformId IS NULL) AND p.PayeeStsId = 1 AND (p.PlatformPayeeId = @platformPayeeId OR @platformPayeeId IS NULL) AND pba.PayeeBankStsId=1 AND (a.Gcid = @Gcid OR @Gcid IS NULL) OPTION (RECOMPILE). This will facilitate a sargable expression and eliminate the predicate entirely when the parameter is NULL.
    – Dan Guzman
    Commented Jan 4 at 23:16
  • 2
    @DanGuzman that comment is an awfully good answer. Look how many upvotes it has 😘 Commented Jan 5 at 0:13
  • appritiate the answers. I know the ISNULL is the root of the problem because it doesn't use the index in the execution plan ( it does INDEX SCAN instead of INDEX SEEK). i understand why it does that as the ISNULL logic is equivalent to an OR logic. I've check the execution plan and the part that cost the most is using the ``` a.Gcid = ISNULL(@Gcid, a.Gcid) ``` I've added a check to see if @Gcid is null IF (@Gcid IS NULL) ...query ... ELSE i've duplicated the query
    – dexon
    Commented Jan 5 at 3:21
  • This is known as a "kitchen sink" or "catch all" query Commented Jan 5 at 11:57

2 Answers 2

5

door #1

You can slap an OPTION(RECOMPILE) hint on the end of your query. The downside here is that SQL Server will have to compile a plan for it each time, and you won't have much of a trail in the plan cache (which isn't as big of a deal if you have Query Store enabled).

The upside is that every time this runs, the optimizer will effectively execute your query with the literal values replacing parameter or variable placeholders. This will allow for seeks when indexes are suitable.

door #2

You can use parameterized dynamic SQL to generate a where clause for any non-NULL parameters. Here's a loose example based on the limited information you've provided.

DECLARE
    @s nvarchar(MAX) = N'',
    @parameters nvarchar(MAX) = N'@PlatformId integer, @platformPayeeId integer, @Gcid integer';

SELECT
    @s += N'
SELECT
    columns
/*Put the name of your stored procedure in a comment so people know where it comes from*/
FROM dbo.YourTable AS p
JOIN dbo.YourOtherTable AS pba
  ON p.Id = pba.Id
JOIN dbo.YourOtherOtherTable AS a
  ON p.Id = a.Id
WHERE p.PayeeStsId = 1
AND   pba.PayeeBankStsId = 1';

IF @PlatformId IS NOT NULL
BEGIN
    SELECT
        @s += N'
AND   p.PlatformId = @PlatformId'
END;

IF @platformPayeeId IS NOT NULL
BEGIN
    SELECT
        @s += N'
AND   p.PlatformPayeeId = @platformPayeeId'
END;

IF @Gcid IS NOT NULL
BEGIN
    SELECT
        @s += N'
AND   a.Gcid = @Gcid'
END;

EXEC sys.sp_executesql
    @s,
    @parameters,
    @PlatformId,
    @platformPayeeId,
    @Gcid;

You'll need to adapt it to your specific situation, of course, but this should get you going in the right direction.

2
  • Am I right to think that this will use different execution plans based on the presence of the parameters?
    – betelgeuce
    Commented Jan 5 at 15:52
  • 1
    @betelgeuce yes, depending on what indexes are available, etc., but that may be the right choice. Commented Jan 5 at 16:13
-2

From what I understand a function is applied to a column, SQL Server often cannot use the index efficiently, leading to a full index scan. I've seen this in a past data conversion I ran where the inclusion o functions into WHERE and SELECT clauses just crippled my scripts.

The obvious solution to this is not to use the IsNull() function on an indexed column. One way is to precompute value of PlatformID as you are passing this in as a variable and provide the computed result into your query:

SET @DefaultPlatformAccountId = @PlatformAccountId;
            
IF @DefaultPlatformAccountId  IS NULL 
    SET @DefaultPlatformAccountId = 
    <INSERT LOGIC TO OBTAIN PLATFORMID HERE FOR WHEN IT IS NULL HERE>
    

Which leads to something like this:

WHERE   p.PlatformId = @DefaultPlatformAccountId  
and p.PayeeStsId     = 1
and p.PlatformPayeeId   = ISNULL(@platformPayeeId, p.PlatformPayeeId)
and pba.PayeeBankStsId=1
and a.Gcid = ISNULL(@Gcid, a.Gcid)

Obviously also precompute the value for: p.PlatformPayeeId as well :-)

5
  • This would return different and incorrect results from OP's original query.
    – J.D.
    Commented Jan 5 at 13:10
  • Updated the first part to make the precomputation more explicit. The point is to precompute the value rather than let an index scan take place. :-)
    – betelgeuce
    Commented Jan 5 at 13:14
  • 2
    It's not possible to achieve what OP wants using the syntax in your first part, so it is still wrong unfortunately. This is because SET @DefaultPlatformAccountId = can only be set to a single scalar value, but OP wants all of the PlatformIds in the table (when the variable is null), which would be multiple values. A temp table used with some crafty syntax may be a better solution than the scalar variable @DefaultPlatformAccountId.
    – J.D.
    Commented Jan 5 at 13:22
  • I think this is the point we disagree. ISNULL in the WHERE clause provided is checking a variable\script parameter called @ PlatformId and if this parameter is null it will sub in the , p.PlatformId from the result set. The point of precomputing the parameter being evaluated for null still stands. It needs to be initialised somewhere outside the Query. This is My point still stands, remove the check for ISNULL and do the ISNULL where you introduce the variable @ PlatformId. Thanks for the feedback.
    – betelgeuce
    Commented Jan 5 at 13:46
  • Unfortunately I didn't quite understand the second half of your comment but your first half of your comment explains why your answer can't solve OP's problem and agrees with my previous comment. Particularly when you said "and if this parameter is null it will sub in the , p.PlatformId from the result set. Precisely! The results set is multiple values, which is not possible to do with a single scalar value. OP's code: WHERE p.PlatformId = ISNULL(@PlatformId, p.PlatformId) logically means if the @PlatformId is null then don't filter on p.PlatformId at all.
    – J.D.
    Commented Jan 5 at 14:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.