Optimierung von Datenbankmodellen–SARGable Abfragen

Ich bekam heute eine recht interessante Aufgabe auf den Tisch. Ein Kunde beklagte sich über die schlechte Ausführungsgeschwindigkeit einer Abfrage, der er von einem Programmierer erhalten hatte. Mir wurde der Code für die View zugeschickt und das Problem war sehr schnell gefunden. Statt eines Indexseek hat die Abfrage nur einen Indexscan durchgeführt. Ursache war, das die WHERE-Klausel keine SARGable Argumente verwendete. Mit diesem Artikel möchte ich aufzeigen, was SARGable ist und wie es funktioniert.

Problemstellung

Um das Problem zu reproduzieren, wird erneut die Datenstruktur aus den bereits zuvor geschriebenen Artikeln zur Optimierung von Datenmodellen verwendet. Die Relation dbo.tbl_Companies besitzt die folgende Struktur:

CREATE TABLE dbo.tbl_Companies
(
    Id          int IDENTITY(1,1) NOT NULL,
    Name        nvarchar(128) NULL,
    TaxNo       varchar(24)   NULL,
    CostCenter  char(7)       NULL,
    UpdateBy    varchar(20)   NULL
);
GO

CREATE UNIQUE CLUSTERED INDEX ci_tbl_Companies_Id ON dbo.tbl_Companies (Id);
CREATE INDEX ix_tbl_Companies_CostCenter ON dbo.tbl_Companies (CostCenter) INCLUDE (Name, TaxNo);

Der Code der gelieferten View sah - auf die obige Datenstruktur angepasst - wie folgt aus:

SELECT Id, Name, TaxNo, CostCenter FROM dbo.tbl_Companies WHERE LEFT(CostCenter, 1) = 'A'

Da auf der Relation ein Index ix_tbl_Companies_CostCenter liegt, ist zu erwarten, dass dieser Index verwendet wird und ein Indexseek angewendet wird. Der Ausführungsplan zeigt jedoch ein ganz anderes Ergebnis an.

ExecutionPlan - Non SARGable ExecutionPlan - Non SARGable - Details

Man kann erkennen, dass zwar der Index ix_tbl_Companies_CostCenter verwendet wird; jedoch wird statt eines optimalen Indexseek ein Indexscan ausgeführt. Woran liegt das?

Problemanalyse

Die Ursache für dieses Verhalten des Microsoft SQL Servers ist verständlich, da Microsoft SQL Server für JEDE Zeile der Relation dbo.tbl_Companies die Funktion SUBSTRING ausführen muss (siehe Details des Ausführungsplans). Somit handelt es sich um ein "Predicate" aber KEIN "Seek Predicate".

Das gleiche Ergebnis erreicht man auch mit der folgenden Abfrage:

SELECT Id, Name, TaxNo, CostCenter FROM dbo.tbl_Companies WHERE CostCenter LIKE 'C%';

ExecutionPlan - SARGable ExecutionPlan - SARGable - Details

Sehr deutlich wird erkennbar, dass Microsoft SQL Server mit dem Operator LIKE ein "Seek Predicate" bilden kann. In diesem Fall können mengenorientierte Abfragen auf die Relation durchgeführt werden, die durch einen Indexseek beeinflusst werden.

Weitere Beispiele für SARGable Abfragen:

non SARGable SARGable
SELECT * FROM dbo.tbl_Companies WHERE Id + 1 = 10 SELECT * FROM dbo.tbl_Companies WHERE Id = 10
SELECT SId, SortName, InsertDate
FROM dbo.tbl_Stammdaten
WHERE YEAR(InsertDate) = 2012

SELECT SId, SortName, InsertDate
FROM   data.tbl_Stammdaten
WHERE  InsertDate >= CAST('20120101' AS datetime) AND InsertDate < CAST('20130101' AS datetime)

Fazit

Es ist unumgänglich, bei der Suche nach Optimierungsmöglichkeiten auch die Details eines Abfrageplans zu beachten. Es sollte immer das Ziel bei Einschränkungen mittels WHERE-Klausel sein, eine SEEK-Einschränkung (Predicate) zu erzielen. Funktionen und Operationen von Attributen verhindern in der Regel diesen Optimierungsschritt.

Herzlichen Dank für's Lesen