T-SQL 性能问题
适用于:SQL Server Azure SQL 数据库 Azure SQL 托管实例 Microsoft Fabric SQL 数据库
在数据库项目中分析 T-SQL 代码时,可能会将一个或多个警告归类为性能问题。 应解决性能问题,以避免出现以下情况:
- 执行代码时会发生表扫描。
一般情况下,如果表包含的数据太少,扫描不会导致性能大幅下降,则可能会抑制性能问题。
提供的规则将确定以下性能问题:
- SR0004:避免在 IN 谓词中使用没有索引的列作为测试表达式
- SR0005:避免在 LIKE 谓词中使用以“%”开头的模式
- SR0006:将列引用移到比较运算符的一侧以使用列索引
- SR0007:对表达式中的可为 null 的列使用 ISNULL(列,default_value)
- SR0015:从 WHERE 谓词中提取确定性函数调用
SR0004:避免在 IN 谓词中使用没有索引的列作为测试表达式
如果使用 WHERE 子句引用未作为 IN 谓词的一部分编制索引的一个或多个列,则会导致表扫描。 表扫描会降低性能。
如何解决冲突
若要解决此问题,必须作出以下更改之一:
- 更改 IN 谓词以仅引用具有索引的列。
- 向 IN 谓词引用且尚无索引的任何列添加索引。
示例
在此示例中,一个简单的 SELECT 语句将引用没有索引的列 [c1]。 第二个语句将定义一个索引,你可以添加该索引来解决此警告。
CREATE PROCEDURE [dbo].[Procedure3WithWarnings]
AS
SELECT [Comment]
FROM [dbo].[Table2]
WHERE [c1] IN (1, 2, 3)
CREATE INDEX [IX_Table2_C1]
ON [dbo].[Table2] (c1);
SR0005:避免在 LIKE 谓词中使用以“%”开头的模式
如果使用包含 LIKE 谓词(如“%pattern string”)的 WHERE 子句搜索可能在列中的任何位置出现的文本,则可能会导致表扫描。
如何解决冲突
若要解决此问题,应更改搜索字符串,使其以不是通配符 (%) 的字符开头,或者应创建全文检索。
示例
在第一个示例中,SELECT 语句会导致表扫描,因为搜索字符串以通配符开头。 在第二个示例中,该语句会导致索引查找,因为搜索字符串不以通配符开头。 索引查找仅会检索与 WHERE 子句匹配的行。
SELECT [dbo].[Table2].[ID], [dbo].[Table2].[c1], [dbo].[Table2].[c2], [dbo].[Table2].[c3], [dbo].[Table2].[Comment]
FROM dbo.[Table2]
WHERE Comment LIKE '%pples'
SELECT [dbo].[Table2].[ID], [dbo].[Table2].[c1], [dbo].[Table2].[c2], [dbo].[Table2].[c3], [dbo].[Table2].[Comment]
FROM dbo.[Table2]
WHERE Comment LIKE 'A%'
SR0006:将列引用移到比较运算符的一侧以使用列索引
如果代码比较包含列引用的表达式,则可能会导致表扫描。
如何解决冲突
若要解决此问题,必须重新处理比较,以使列引用单独显示在比较运算符的一侧,而不是在表达式内部显示。 运行在比较运算符的一侧单独拥有列引用的代码时,SQL Server 可以使用列索引,并且不执行表扫描。
示例
在第一个过程中,WHERE 子句在表达式中包含列 [c1] 作为比较的一部分。 在第二个过程中,比较结果相同,但从不需要表扫描。
CREATE PROCEDURE [dbo].[Procedure3WithWarnings]
@param1 int
AS
SELECT [c1], [c2], [c3], [Comment]
FROM [dbo].[Table2]
WHERE ([c1] + 5 > @param1)
CREATE PROCEDURE [dbo].[Procedure3Fixed]
@param1 int
AS
SELECT [c1], [c2], [c3], [Comment]
FROM [dbo].[Table2]
WHERE ([c1] > (@param1 - 5))
SR0007:对表达式中的可为 null 的列使用 ISNULL(列,default_value)
如果代码将两个 NULL
值或一个 NULL
值与任何其他值进行比较,则代码将返回未知结果。
如何解决冲突
应通过整合可以包含 NULL
函数中的 NULL
值的每个列来显式指示如何处理比较表达式中的 ISNULL
值。
示例
此示例显示了一个简单的表定义和两个存储过程。 该表包含一个列 c2
,该列可以包含一个 NULL
值。 第一个过程 ProcedureWithWarning
将 c2
与常数值进行比较。 第二个过程通过对 c2
函数的调用整合 ISNULL
来修复问题。
CREATE TABLE [dbo].[Table1]
(
[ID] INT NOT NULL IDENTITY(0, 1),
[c1] INT NOT NULL PRIMARY KEY,
[c2] INT
)
ON [PRIMARY]
CREATE PROCEDURE [dbo].[ProcedureWithWarning]
AS
BEGIN
SELECT COUNT(*) FROM [dbo].[Table1]
WHERE [c2] > 2;
END
CREATE PROCEDURE [dbo].[ProcedureFixed]
AS
BEGIN
SELECT COUNT(*) FROM [dbo].[Table1]
WHERE ISNULL([c2],0) > 2;
END
SR0015:从 WHERE 谓词中提取确定性函数调用
在 WHERE 谓词中,如果函数调用的值不依赖于所选数据,则该函数调用是确定性的。 此类调用可能会导致不必要的表扫描,从而降低数据库性能。
如何解决冲突
若要解决此问题,可以将调用的结果分配给用于 WHERE 谓词的变量。
示例
在第一个示例中,存储过程在 WHERE 谓词中包含确定性函数调用 ABS(@param1)
。 在第二个示例中,临时变量保存调用的结果。
CREATE PROCEDURE [dbo].[Procedure2WithWarning]
@param1 INT = 0,
AS
BEGIN
SELECT [c1], [c2], [c3], [SmallString]
FROM [dbo].[Table1]
WHERE [c2] > ABS(@param1)
END
CREATE PROCEDURE [dbo].[Procedure2Fixed]
@param1 INT = 0,
AS
BEGIN
DECLARE @AbsOfParam1 INT
SET @AbsOfParam1 = ABS(@param1)
SELECT [c1], [c2], [c3], [SmallString]
FROM [dbo].[Table1]
WHERE [c2] > @AbsOfParam1
END