SQL Server 中的 Sargable 查询。






4.56/5 (18投票s)
在本文中,我们将讨论如何在 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。在实施上述提示后检查查询的执行计划,以充分利用您的索引。
历史
在此处保持您所做的任何更改或改进的实时更新。