【问题标题】:performance impact of making where clause dummy - SQL Server使 where 子句虚拟的性能影响 - SQL Server
【发布时间】:2017-07-27 08:21:23
【问题描述】:

我需要了解以下编写查询的方法对性能的影响。 假设有一个员工表。要求是获取特定部门下的员工列表,并且用户可以选择通过提供城市/位置来过滤结果集。

declare @dept varchar(10) = 'ABC', @city varchar(10) 
select * from employee where dept = @dept and city = isnull(@city, city)

这样好吗?还是我们需要使用传统的 if 逻辑来检查用户是否提供了城市作为输入?

谢谢,
萨巴里什。

【问题讨论】:

    标签: sql performance tsql where-clause


    【解决方案1】:

    我记得在某处读到以下语法比调用 ISNULL() 更快:

    select * from employee where dept = @dept and (@city IS NULL OR @city = city)
    

    这与 SQL 编译器有关,因为它知道如果 @city 为空,它可以忽略括号中的表达式。

    对不起,但不知道我在哪里读到的(这是前一段时间的),否则我会正确引用它。

    【讨论】:

      【解决方案2】:

      解决空值性能问题的最有效方法是尝试通过默认值避免空值。在你的情况下应该是好的尝试类似:

      declare @dept varchar(10) = 'ABC', @city varchar(10) = 'unknown' 
      SELECT * 
      FROM employee 
      WHERE dept = @dept AND 
            @city = 'unknown'
      UNION
      SELECT * 
      FROM employee 
      WHERE dept = @dept AND 
            city = @city AND 
            @city != 'unknown'
      

      为什么? 基数估计器无法估计查询返回的正确行数,它会导致执行计划对这个特定查询不利。避免空值,一切都会很好 B-)

      【讨论】:

        【解决方案3】:

        如果“城市”列上有单独的非聚集索引,那么@Jonathan 提供的答案肯定会提高性能。如果两者都不是,则执行计划将导致 SCAN。如果您有非聚集索引,那么 Jonathan 的方法将执行 SEEK 而不是 SCAN,这在性能方面会很好。

        让我尝试用下表中的示例解释为什么会出现这种情况:为了便于使用,我没有考虑两个谓词 dept 和 city,而是只考虑 City。

        考虑下面的员工表:

        CREATE TABLE [dbo].[Employee](
            [EmployeeId] [int] NULL,
            [EmployeeName] [varchar](20) NULL,
            [Dept] [varchar](15) NULL,
            [city] [varchar](15) NULL
        ) ON [PRIMARY]
        GO
        
        --Creating Clustered Index on Id
        CREATE CLUSTERED INDEX [CI_Employee_EmployeeId] ON [dbo].[Employee] ( [EmployeeId] ASC)
        
        --Loading Data
        

        加载样本数据

        Insert into Employee 
            Select top (10000)  EmployeeId = Row_Number() over (order by (Select NULL))
                    ,EmployeeName = Concat ('Name ',Row_Number() over (order by (Select NULL)))
                    ,Dept = Concat ('Dept ',(Row_Number() over (order by (Select NULL))) % 50)
                    ,City = Concat ('City ',Row_Number() over (order by (Select NULL)))
                    from master..spt_values s1, master..spt_values s2
        

        现在使用普通谓词执行简单查询:

        Declare @city varchar(15) = 'City 1500'
        Select * from Employee where city = @city
        --It Does Clustered Index Scan
        

        现在在城市上创建一个非聚集索引

        --Now adding Index on City
        Create NonClustered Index NCI_Employee_City on dbo.Employee (city)
        
        Declare @city varchar(15) = 'City 1500'
        Select * from Employee where city =  @city
        --It Does Index Seek
        

        现在来到你的 isnull 函数 由于它强制对每个城市使用 SCAN,如下所示

        Declare @city varchar(15) = 'City 1500'
        Select * from Employee where city =  isnull(@city, City)
        go
        Declare @city varchar(15) = 'City 1500'
        Select * from Employee where city is null or city = @city
        

        如果您查看整体百分比,则 IsNull 函数需要更多。

        因此,如果您有索引,所有这些都会有所帮助,否则无论如何都会被扫描。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-06-04
          • 2011-06-21
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多