使 where 子句虚拟的性能影响 - SQL 服务器
performance impact of making where clause dummy - SQL Server
我需要了解以下编写查询的方法对性能的影响。
假设有一名员工 table。要求是获取特定部门下的员工列表,用户可以选择通过提供 city/location 来过滤结果集。
declare @dept varchar(10) = 'ABC', @city varchar(10)
select * from employee where dept = @dept and city = isnull(@city, city)
这样可以吗?还是我们需要使用传统的 if 逻辑来检查用户是否提供城市作为输入?
谢谢,
萨巴里什
我记得在某处读到以下语法比调用 ISNULL() 更快:
select * from employee where dept = @dept and (@city IS NULL OR @city = city)
这与 SQL 编译器有关,它有效地知道如果@city 为空,它可以忽略括号中的表达式。
抱歉,但不知道我在哪里读到这篇文章(很久以前),否则我会正确引用它。
解决空值性能问题的最有效方法是尽量避免默认值出现空值。在你的情况下应该是好的尝试这样的事情:
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'
为什么?
基数估计器无法估计查询 returns 的正确行数,这会导致执行计划对于此特定查询应该是错误的。避免空值,一切都会很好 B-)
如果 'City' 列上有单独的非聚集索引,@Jonathan 提供的答案肯定会提高性能。如果不是两个执行计划将导致 SCAN。如果您有非聚集索引,那么 Jonathan 的方法将执行 SEEK 而不是 SCAN,这在性能方面会很好。
让我试着用下面的示例来解释为什么会出现这种情况 table:为了便于使用,我没有考虑两个谓词 dept 和 city,而是只考虑 City。
考虑以下员工 table:
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
正在为 city 创建一个非聚集索引
--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 函数所需的总体百分比。
因此,如果您有索引,所有这些都会有所帮助,否则无论如何都会被扫描。
我需要了解以下编写查询的方法对性能的影响。 假设有一名员工 table。要求是获取特定部门下的员工列表,用户可以选择通过提供 city/location 来过滤结果集。
declare @dept varchar(10) = 'ABC', @city varchar(10)
select * from employee where dept = @dept and city = isnull(@city, city)
这样可以吗?还是我们需要使用传统的 if 逻辑来检查用户是否提供城市作为输入?
谢谢,
萨巴里什
我记得在某处读到以下语法比调用 ISNULL() 更快:
select * from employee where dept = @dept and (@city IS NULL OR @city = city)
这与 SQL 编译器有关,它有效地知道如果@city 为空,它可以忽略括号中的表达式。
抱歉,但不知道我在哪里读到这篇文章(很久以前),否则我会正确引用它。
解决空值性能问题的最有效方法是尽量避免默认值出现空值。在你的情况下应该是好的尝试这样的事情:
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'
为什么? 基数估计器无法估计查询 returns 的正确行数,这会导致执行计划对于此特定查询应该是错误的。避免空值,一切都会很好 B-)
如果 'City' 列上有单独的非聚集索引,@Jonathan 提供的答案肯定会提高性能。如果不是两个执行计划将导致 SCAN。如果您有非聚集索引,那么 Jonathan 的方法将执行 SEEK 而不是 SCAN,这在性能方面会很好。
让我试着用下面的示例来解释为什么会出现这种情况 table:为了便于使用,我没有考虑两个谓词 dept 和 city,而是只考虑 City。
考虑以下员工 table:
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
正在为 city 创建一个非聚集索引
--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 函数所需的总体百分比。
因此,如果您有索引,所有这些都会有所帮助,否则无论如何都会被扫描。