T-SQL-Leistungsprobleme
Gilt für: SQL Server Azure SQL-Datenbank Azure SQL Managed Instance
Wenn Sie den T-SQL-Code in Ihrem Datenbankprojekt analysieren, wird möglicherweise mindestens eine Warnung als Leistungsprobleme kategorisiert. Sie sollten Leistungsprobleme beheben, um die folgende Situation zu vermeiden:
- Beim Ausführen des Codes erfolgt ein Tabellenscan.
Im Allgemeinen können Sie ein Leistungsproblem unterdrücken, wenn die Tabelle so wenig Daten enthält, dass ein Scan die Leistung nicht erheblich reduziert.
Die bereitgestellten Regeln identifizieren die folgenden Leistungsprobleme:
- SR0004: Vermeiden Sie die Verwendung von Spalten, die keine Indizes als Testausdrücke in IN-Prädikaten aufweisen
- SR0005: Vermeiden Sie die Verwendung von Mustern, die mit „%“ in LIKE-Prädikaten beginnen
- SR0006: Verschieben eines Spaltenverweises auf eine Seite eines Vergleichsoperators zur Verwendung eines Spaltenindex
- SR0007: Verwenden von ISNULL (Spalte, default_value) für nullfähige Spalten in Ausdrücken
- SR0015: Extrahieren deterministischer Funktionsaufrufe aus WHERE-Prädikaten
SR0004: Vermeiden Sie die Verwendung von Spalten, die keine Indizes als Testausdrücke in IN-Prädikaten aufweisen
Sie verursachen einen Tabellenscan, wenn Sie eine WHERE-Klausel verwenden, die auf eine oder mehrere Spalten verweist, die nicht als Teil eines IN-Prädikats indiziert sind. Der Tabellenscan reduziert die Leistung.
Behandeln von Verstößen
Sie müssen zum Lösen dieses Problems eine der folgenden Änderungen vornehmen:
- Ändern Sie das IN-Prädikat so, dass nur auf die Spalten verwiesen wird, die einen Index aufweisen.
- Fügen Sie einen Index zu jeder Spalte hinzu, auf die das IN-Prädikat verweist und die noch keinen Index hat.
Beispiel
In diesem Beispiel verweist eine einfache SELECT-Anweisung auf eine Spalte, [c1], die keinen Index hat. Die zweite Anweisung definiert einen Index, den Sie hinzufügen können, um diese Warnung zu beheben.
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: Vermeiden Sie die Verwendung von Mustern, die mit „%“ in LIKE-Prädikaten beginnen
Sie können einen Tabellenscan verursachen, wenn Sie eine WHERE-Klausel verwenden, die ein LIKE-Prädikat wie „%pattern string“ enthält, um nach Text zu suchen, der an einer beliebigen Stelle in einer Spalte auftreten kann.
Behandeln von Verstößen
Um dieses Problem zu beheben, sollten Sie die Suchzeichenfolge so ändern, dass sie mit einem Zeichen beginnt, das kein Wildcard (%) ist, oder Sie sollten einen Volltextindex erstellen.
Beispiel
Im ersten Beispiel bewirkt die SELECT-Anweisung einen Tabellenscan, da die Suchzeichenfolge mit einem Wildcard-Zeichen beginnt. Im zweiten Beispiel bewirkt die Anweisung eine Indexsuche, da die Suchzeichenfolge nicht mit einem Platzhalterzeichen beginnt. Eine Indexsuche ruft nur die Zeilen ab, die der WHERE-Klausel entsprechen.
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: Verschieben eines Spaltenverweises auf eine Seite eines Vergleichsoperators zur Verwendung eines Spaltenindex
Ihr Code könnte einen Tabellenscan verursachen, wenn ein Ausdruck verglichen wird, der einen Spaltenverweis enthält.
Behandeln von Verstößen
Um dieses Problem zu beheben, müssen Sie den Vergleich umarbeiten, damit der Spaltenverweis allein auf einer Seite des Vergleichsoperators statt innerhalb eines Ausdrucks angezeigt wird. Wenn Sie den Code ausführen, der den Spaltenverweis allein auf einer Seite des Vergleichsoperators enthält, kann SQL Server den Spaltenindex verwenden, und es wird kein Tabellenscan ausgeführt.
Beispiel
In der ersten Prozedur enthält eine WHERE-Klausel die Spalte [c1] in einem Ausdruck als Teil eines Vergleichs. Im zweiten Verfahren sind die Vergleichsergebnisse identisch, erfordern aber nie einen Tabellenscan.
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: Verwenden von ISNULL (Spalte, default_value) für nullfähige Spalten in Ausdrücken
Wenn Ihr Code zwei NULL
-Werte oder einen NULL
-Wert mit einem anderen Wert vergleicht, gibt ihr Code ein unbekanntes Ergebnis zurück.
Behandeln von Verstößen
Sie sollten ausdrücklich angeben, wie NULL
-Werte in Vergleichsausdrücken behandelt werden sollen, indem Sie jede Spalte, die einen NULL
-Wert enthalten kann, in eine ISNULL
-Funktion einschließen.
Beispiel
Dieses Beispiel zeigt eine einfache Tabellendefinition und zwei gespeicherte Prozeduren. Die Tabelle enthält eine Spalte, c2
, die einen NULL
-Wert enthalten kann. Die erste Prozedur, ProcedureWithWarning
, vergleicht c2
mit einem Konstantenwert. Die zweite Prozedur behebt das Problem durch Umbruch von c2
mit einem Aufruf der ISNULL
-Funktion.
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: Extrahieren deterministischer Funktionsaufrufe aus WHERE-Prädikaten
In einem WHERE-Prädikat ist ein Funktionsaufruf deterministisch, wenn sein Wert nicht von den ausgewählten Daten abhängt. Solche Aufrufe könnten unnötige Tabellenscans verursachen, wodurch die Datenbankleistung verringert wird.
Behandeln von Verstößen
Um dieses Problem zu beheben, können Sie das Ergebnis des Aufrufs einer Variablen zuweisen, die Sie im WHERE-Prädikat verwenden.
Beispiel
Im ersten Beispiel enthält die gespeicherte Prozedur einen deterministischen Funktionsaufruf,ABS(@param1)
, im WHERE-Prädikat. Im zweiten Beispiel enthält eine temporäre Variable das Ergebnis des Aufrufs.
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