T-SQL 效能問題
適用於:Microsoft Fabric 中的 SQL ServerAzure SQL 資料庫 Azure SQL 受控執行個體 SQL 資料庫
當您在資料庫專案中分析 T-SQL 程式碼時,可能會將一或多個警告分類為效能問題。 您應該解決效能問題,以避免下列情況:
- 執行程式碼時,就會發生資料表掃描。
一般而言,如果資料表包含的資料很少,掃描不會造成效能大幅下降,您有可能隱藏效能問題。
提供的規則可識別下列效能問題:
- SR0004:避免在 IN 述詞中使用沒有索引的資料行作為測試運算式
- SR0005:避免在 LIKE 述詞中使用開頭為 "%" 的模式
- SR0006:將資料行參考移至比較運算子的一端,以使用資料行索引
- SR0007:在運算式中的可為 Nulll 資料行上使用 ISNULL(column, 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 述詞的 WHERE 子句,例如 '%pattern string' 來搜尋資料行中任何地方可能發生的文字,則可能會造成資料表掃描。
如何修正違規
若要解決此問題,您應該變更搜尋字串,使其以非萬用字元 (%) 的字元開頭,或者您應該建立全文檢索索引。
範例
在第一個範例中,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:在運算式中的可為 Nulll 資料行上使用 ISNULL(column, default_value)
如果您的程式碼將兩 NULL
個值或一個 NULL
值與任何其他值進行比較,則程式碼會傳回未知的結果。
如何修正違規
您應該明確地指出如何處理 NULL
比較運算式中的值,方法是包裝可包含 NULL
函式中 ISNULL
值的每個資料行。
範例
此範例顯示簡單的資料表定義和兩個預存程序。 資料表包含資料行 c2
,該資料行可以包含 NULL
值。 第一個程序 ProcedureWithWarning
會將 c2
與常數值進行比較。 第二個程序會透過呼叫 ISNULL
函式來包裝 c2
,以修正問題。
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