How to Tune SQL Statement with CASE Expression for SQL Server II?

Discussion in 'SQL Server' started by Richard To, Aug 19, 2021.

  1. Richard To

    Richard To Member

    Joined:
    Sep 18, 2017
    Messages:
    63
    Likes Received:
    21
    Trophy Points:
    8
    Gender:
    Male
    We have discussed how to tune a CASE expression SQL with hardcoded literals in my last blog:

    How to Tune SQL Statement with CASE Expression for SQL Server I? | Go4Expert
    Code:
    SELECT *
      FROM EMPLOYEE
    WHERE
     CASE
      when emp_id  < 1001000 then 'Old Employee'
      when emp_dept <'B'      then 'Old Department'
    ELSE 'Normal'
     END = 'Old Employee'
    
    If I change the hardcoded literal to a @var, what will be the performance of the last blog’s rewritten SQL?
    Code:
    SELECT *
      FROM EMPLOYEE
    WHERE
     CASE
      when emp_id  < 1005000 then 'Old Employee'
      when emp_dept <'B'      then 'Old Department'
    ELSE 'Normal'
     END = @var
    
    I use the same method in my last blog to rewrite this SQL into the following multiple OR syntax, but the SQL Server optimizer change back to a full table scan of the EMPLOYEE table. It is because the SQL Server cannot do a good cardinality estimation of the variable of @var.
    Code:
    select *
    from  EMPLOYEE
    where  emp_id < 1005000
          and 'Old Employee' = @var
          or not ( emp_id < 1005000 )
              and emp_dept < 'B'
              and 'Old Department' = @var
          or not ( emp_id < 1005000 )
              and not ( emp_dept < 'B' )
              and 'Normal' = @var
    
    We can rewrite the CASE expression into the following syntax with multiple UNION ALL statements, this syntax is more complicated than the rewrite with multiple OR conditions in my last blog. But it can make SQL Server improve the query plan to be more efficient.
    Code:
    select *
    from  EMPLOYEE
    where  emp_id < (select 1005000)
           and 'Old Employee' = @var
    union all
    select *
    from  EMPLOYEE
    where  ( not ( emp_id < 1005000
                   and 'Old Employee' = @var )
             or @var is null )
           and emp_id >= 1005000
           and emp_dept < 'B'
           and 'Old Department' = @var
    union all
    select *
    from  EMPLOYEE
    where  ( not ( emp_id < 1005000
                   and 'Old Employee' = @var )
             or @var is null )
           and ( not ( emp_id >= 1005000
                       and emp_dept < 'B'
                       and 'Old Department' = @var )
                 or @var is null )
           and emp_id >= 1005000
           and emp_dept >= 'B'
           and 'Normal' = @var
    
    Here is the query plan of the rewritten SQL and the speed is 0.448 seconds. It is 5 times better than the original syntax. People may think that there are two table scan operations of EMPLOYEE that will slow down the whole process, but actually, the corresponding filter operations will stop the table scan operations immediately due to the filter conditions 'Normal' = @var and 'Old Department' = @var will not be satisfied. This kind of query plan cannot be generated by SQL Server’s internal SQL optimizer, it means that you cannot use Hints injection to get this query plan.
    [​IMG]
    This kind of rewrite can be achieved by Tosska SQL Tuning Expert for SQL Server automatically.

    Tosska SQL Tuning Expert (TSES™) for SQL Server® - Tosska Technologies Limited
    [​IMG]
     

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice