T-SQL 性能问题

适用于:SQL Server Azure SQL 数据库 Azure SQL 托管实例 Microsoft Fabric SQL 数据库

在数据库项目中分析 T-SQL 代码时,可能会将一个或多个警告归类为性能问题。 应解决性能问题,以避免出现以下情况:

  • 执行代码时会发生表扫描。

一般情况下,如果表包含的数据太少,扫描不会导致性能大幅下降,则可能会抑制性能问题。

提供的规则将确定以下性能问题:

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 值。 第一个过程 ProcedureWithWarningc2 与常数值进行比较。 第二个过程通过对 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