65.9K
CodeProject 正在变化。 阅读更多。
Home

SQL Server 中的 Sargable 查询。

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.56/5 (18投票s)

2014年10月13日

CPOL

3分钟阅读

viewsIcon

44950

在本文中,我们将讨论如何在 SQL Server 中编写 Sargable 查询。

引言

有多种方法可以提高 SQL 查询的性能。Sargable 查询是其中一种方法。

描述

数据库的索引可以神奇地提高查询性能。但有时由于不良做法,索引没有被使用。有时我们注意到,即使创建了适当的索引,SQL Server 也无法在所需的列上使用索引查找。这里索引被忽略了。

理解 Sargable 表达式的概念可以神奇地提高查询性能。SARG 是“可搜索参数”的首字母缩写。根据维基百科,Sargable 被定义为“在关系数据库中,如果 DBMS 引擎可以利用索引来加速查询的执行,则查询中的条件(或谓词)被称为 sargable。该术语源自“可搜索参数”的缩写”。在此处查找链接

因此,如果我们可以充分利用列中可用的索引,则查询将被称为 Sargable 查询。这里查询使用索引查找操作。

索引查找
  在这里,SQL Server 直接使用索引的 B 树结构来获取匹配的记录。它速度很快,并且适用于具有海量数据的表。
索引扫描
  在这里,SQL Server 扫描/读取表的所有记录以返回所需的行。它很慢。但是对于数据量小的表,需要获取所有记录时,此过程是可以的。

通常,当我们在 WHERE 子句中包含列中的函数/操作时,查询将变为 Non-Sargable。 几个 Non-sargable  搜索参数通常会阻止(但并非总是)查询优化器使用有用的索引来执行搜索,这些参数包括“IS NULL”、“<>”、“!=”、“!>”、“!<”、“NOT”、“NOT EXISTS”、“NOT IN”、“NOT LIKE”和“LIKE ‘%test′”。

完成查询后,务必检查其执行计划,以检查查询是否正在使用可用的索引。

让我们创建测试表以演示 Sargable 查询。

CREATE TABLE SargableDemo
(
[ID] INT IDENTITY(1, 1) NOT NULL,
[DealerName] NVARCHAR(100) NULL,
[OrderID] INT NOT NULL,
[Date] Datetime not null,
)

让我们向表中插入几行。

INSERT INTO SargableDemo(DealerName,OrderID,[Date]) VALUES('Toyata',11,GETDATE())
INSERT INTO SargableDemo(DealerName,OrderID,[Date]) VALUES('Toyata',12,dateadd(YEAR,1, getdate()))
INSERT INTO SargableDemo(DealerName,OrderID,[Date]) VALUES('Maruti',13,dateadd(DAY,1, getdate()))
INSERT INTO SargableDemo(DealerName,OrderID,[Date]) VALUES('Ford',1,dateadd(YEAR,2, getdate()))
INSERT INTO SargableDemo(DealerName,OrderID,[Date]) VALUES('Toyata',21,dateadd(DAY,21, getdate()))
INSERT INTO SargableDemo(DealerName,OrderID,[Date]) VALUES('BMW',41,dateadd(DAY,4, getdate()))
INSERT INTO SargableDemo(DealerName,OrderID,[Date]) VALUES('Toyata',51,dateadd(YEAR,5, getdate()))
INSERT INTO SargableDemo(DealerName,OrderID,[Date]) VALUES('FORD',71,dateadd(DAY,7, getdate()))
INSERT INTO SargableDemo(DealerName,OrderID,[Date]) VALUES('Toyata',81,dateadd(DAY,9, getdate()))
INSERT INTO SargableDemo(DealerName,OrderID,[Date]) VALUES('BMW',91,dateadd(YEAR,2, getdate()))

通配符比较

让我们在 DealerName 列上创建一个索引。

CREATE NONCLUSTERED INDEX IX_SargableDemo_DealerName 
ON SargableDemo(DealerName); 
GO

在这里,我们希望找到所有名称为“Toyota”的经销商。下面是非 Sargable 和 Sargable 的方法。
Non-Sargable

SELECT DealerName FROM SargableDemo WHERE DealerName Like '%Toyota'

执行计划在这里。

这里忽略了 DealerName 列上的索引,导致索引扫描。

Sargable

SELECT DealerName FROM SargableDemo WHERE DealerName Like 'Toyota%'

执行计划在这里。

这里使用了 DealerName 列上的索引,导致索引查找。

算术运算符
 让我们用算术运算符做一个演示。

让我们在 OrderID 列上创建一个索引

CREATE NONCLUSTERED INDEX IX_SargableDemo_OrderID 
ON SargableDemo(OrderID); 
GO

这里我们将包含一个算术运算符在 WHERE 子句中。下面是非 Sargable 和 Sargable 的方法。 

Non-Sargable

SELECT OrderID FROM SargableDemo WHERE OrderID *3 = 33000

执行计划在这里。

这里忽略了 OrderID 列上的索引,导致索引扫描。

Sargable

SELECT OrderID FROM SargableDemo WHERE OrderID = 33000/3

执行计划在这里。

这里使用了 OrderID 列上的索引,导致索引查找。

同样,使用 ABS() 函数会使查询变为 non-sergable。

标量函数
 让我们用 YEAR() 函数做一个演示。

让我们在 Date 列上创建一个索引。

CREATE NONCLUSTERED INDEX IX_SargableDemo_Date 
ON SargableDemo([Date]); 
GO

 

在这里,我们希望找到在特定年份放置的所有记录。 下面是非 Sargable 和 Sargable 的方法。

 Non-Sargable: 

SELECT [Date] FROM SargableDemo WHERE Year([Date]) = 2014

执行计划在这里。

这里忽略了 Date 列上的索引,导致索引扫描。 

Sargable: 

SELECT [Date] FROM SargableDemo WHERE [Date] >= '01-01-2014' AND [Date] < '01-01-2015'

执行计划在这里。

这里使用了 Date 列上的索引,导致索引查找。

这里还有一些例子
Non-Sargable

SELECT... WHERE isNull(FullName,'Jitendra') = 'Jitendra'


Sargable

SELECT... WHERE ((FullName = 'Jitendra') OR (FullName IS NULL))

Non-Sargable

SELECT ... WHERE SUBSTRING(FullName,4) = 'Jite'


Sargable

SELECT... WHERE FullName LIKE 'Jite%'

Non-Sargable

SELECT DealerName FROM SargableDemo WHERE UPPER(DealerName) LIKE 'FORD'

SQL Server 默认不区分大小写,因此在这里使用 UPPER() 和 LOWER() 函数是不好的。

Sargable

SELECT DealerName FROM SargableDemo WHERE DealerName LIKE 'FORD'

关注点

因此,通过在 WHERE 条件中编写 non-sergable 函数/操作的逆函数/操作,我们可以提高查询的性能。 因此,为了避免索引扫描并提高查询性能,请尝试使 WHERE 子句 sargable。在实施上述提示后检查查询的执行计划,以充分利用您的索引。

历史

在此处保持您所做的任何更改或改进的实时更新。

© . All rights reserved.