具有嵌套case语句优化的SQL where子句

时间:2013-10-01 18:33:48

标签: sql sql-server-2008 query-optimization case where-clause

下面是我的WHERE子句,并且有重复(特别是两个@subgroup变量),所以我想知道我能做些什么来优化代码。我的代码工作得很好,但如果我能让它变得更漂亮那就太好了。首先是一个快速解释。

我有五个参数来自asp下拉列表。出于这个问题的目的,我用数据填充了它们:

DECLARE
@DataCollection VARCHAR(50) = '2013/14 - Autumn 1 - Targets',
@StuYear VARCHAR(2) = '11',
@SubjectName VARCHAR(100) ='English',
@TeachingGroup VARCHAR(25) = 'Select All',
@SubGroup VARCHAR(15) = 'FSMYes',
@SubGroup2 VARCHAR(15) = 'SENS'

where语句允许根据下拉列表中的值过滤结果集。

@DataCollection,@ StuYear和@SubjectName都是必需的,并且在下拉列表中有默认值。其余的是可选的。

@subgroup的变量是根据下拉列表中的硬编码值填充的,因此传递参数,如果该参数值与WHERE子句中的条件匹配,则将特定的db值过滤到表的特定列中

这是我的SQL:

WHERE
[StuYear] = @StuYear AND
[DataCollection] = @DataCollection AND
[Name] = @SubjectName AND (
    @TeachingGroup = 'Select All' OR 
    [TeachingGroup] = @TeachingGroup
) AND (
   @SubGroup = 'Select All' OR
   Gender = CASE --Gender
           WHEN @SubGroup = 'GenF' THEN 'F'
           WHEN @SubGroup = 'GenM' THEN 'M'
       END 
   OR
   LEFT(ks2av,1) = CASE --Attainer
           WHEN @SubGroup = 'High' THEN '5'
           WHEN @SubGroup = 'High' THEN '5'
           WHEN @SubGroup = 'Middle' THEN '4'
           WHEN @SubGroup = 'Low' THEN '3'
           WHEN @SubGroup = 'Low' THEN '2'
           WHEN @SubGroup = 'Low' THEN '1'
       END
   OR
   PupilPremium = CASE --Pupil Premium
           WHEN @Subgroup = 'PPYes' THEN 'Yes'
       END 
   OR
   FSM = CASE --Free School Meals
           WHEN @Subgroup = 'FSMYes' THEN 'Yes'
       END
   OR
   SEN = CASE --SEN
           WHEN @Subgroup = 'SENA' THEN 'A'
           WHEN @Subgroup = 'SENP' THEN 'P'
           WHEN @Subgroup = 'SENS' THEN 'S'
           WHEN @Subgroup = 'SENNo' THEN 'N'
       END
   OR
   (@SubGroup='SENYes' AND SEN IN ('A','P','S')) --SEN Yes
   OR
   EAL = CASE --English as an Additional Language
           WHEN @Subgroup = 'EALYes' THEN 'Yes'
       END
   OR
   LAC = CASE --Looked After Children
           WHEN @Subgroup = 'LACYes' THEN 'Yes'
       END
   OR --Gifted & Talented       
   GandT = CASE 
           WHEN @Subgroup = 'GandTYes' THEN 'Yes'
       END
   OR --Gifted Only       
   Gifted = CASE 
           WHEN @Subgroup = 'GiftedYes' THEN 'Yes'
       END
   OR --Talented Only       
   Talented = CASE 
           WHEN @Subgroup = 'TalentedYes' THEN 'Yes'
       END
)AND (
   @subgroup2 = 'Select All' OR
   Gender = CASE --Gender
           WHEN @subgroup2 = 'GenF' THEN 'F'
           WHEN @subgroup2 = 'GenM' THEN 'M'
       END 
   OR
   LEFT(ks2av,1) = CASE --Attainer
           WHEN @subgroup2 = 'High' THEN '5'
           WHEN @subgroup2 = 'High' THEN '5'
           WHEN @subgroup2 = 'Middle' THEN '4'
           WHEN @subgroup2 = 'Low' THEN '3'
           WHEN @subgroup2 = 'Low' THEN '2'
           WHEN @subgroup2 = 'Low' THEN '1'
       END
   OR
   PupilPremium = CASE --Pupil Premium
           WHEN @subgroup2 = 'PPYes' THEN 'Yes'
       END 
   OR
   FSM = CASE --Free School Meals
           WHEN @subgroup2 = 'FSMYes' THEN 'Yes'
       END
   OR
   SEN = CASE --SEN
           WHEN @subgroup2 = 'SENA' THEN 'A'
           WHEN @subgroup2 = 'SENP' THEN 'P'
           WHEN @subgroup2 = 'SENS' THEN 'S'
           WHEN @subgroup2 = 'SENNo' THEN 'N'
       END
   OR
   (@subgroup2='SENYes' AND SEN IN ('A','P','S')) --SEN Yes
   OR
   EAL = CASE --English as an Additional Language
           WHEN @subgroup2 = 'EALYes' THEN 'Yes'
       END
   OR
   LAC = CASE --Looked After Children
           WHEN @subgroup2 = 'LACYes' THEN 'Yes'
       END
   OR --Gifted & Talented       
   GandT = CASE 
           WHEN @subgroup2 = 'GandTYes' THEN 'Yes'
       END
   OR --Gifted Only       
   Gifted = CASE 
           WHEN @subgroup2 = 'GiftedYes' THEN 'Yes'
       END
   OR --Talented Only       
   Talented = CASE 
           WHEN @subgroup2 = 'TalentedYes' THEN 'Yes'
       END
)

1 个答案:

答案 0 :(得分:2)

所有这些OR都倾向于优化得不好,因为将为一条路径选择一个计划,然后在其他几个没有多大意义的地方重新使用。您可以考虑两个选项:

  1. Dynamic SQL。这允许分别编译和优化每个特定的代码路径。您可以使用optimize for ad hoc workloads配置选项阻止计划缓存膨胀,这会阻止新计划在第二次使用之前完全缓存。快速举例:

    DECLARE @sql NVARCHAR(MAX) = N'SELECT ... 
      WHERE StuYear = @StuYear
        AND DataCollection = @DataCollection
        AND Name = @SubjectName';
    
    -- assuming optional parameters can't be NULL:
    IF @TeachingGroup <> 'Select All' THEN 
      SET @sql += N' AND TeachingGroup = @TeachingGroup';
    
    -- several more of these
    
    EXEC sp_executesql @sql,
      N'@StuYear VARCHAR(2), @DataCollection VARCHAR(50),
        @SubjectName VARCHAR(100), @TeachingGroup VARCHAR(25)',
      @StuYear, @DataCollection, @SubjectName, @TeachingGroup;
    
  2. OPTION (RECOMPILE)添加到查询中。这将在整体编译方面比动态SQL花费更多,因为每个版本都不会每次都编译,但这里的额外好处是它还可以阻止参数嗅探(其中不同的参数值可以极大地影响效果)一个选定的计划)。

  3. 顺便说一句,这样的事情:

    SEN = CASE --SEN
           WHEN @Subgroup = 'SENA' THEN 'A'
           WHEN @Subgroup = 'SENP' THEN 'P'
           WHEN @Subgroup = 'SENS' THEN 'S'
           WHEN @Subgroup = 'SENNo' THEN 'N'
       END
    

    可以简化为:

    SEN = CASE WHEN @Subgroup LIKE 'SEN[APSN]%' THEN SUBSTRING(@Subgroup, 4, 1) END