T-Lift: Dynamisches SQL, ohne es von Hand zu schreiben.
Catch-all-Prozeduren sind bequem und bremsen trotzdem. T-Lift, unser Open-Source-Precompiler für T-SQL, macht daraus parametrisiertes dynamisches SQL mit passenden Plänen. Ihr Quelltext bleibt dabei ganz normales, testbares T-SQL.
Das Problem: eine Abfrage für alle Fälle
Suchmasken mit vielen optionalen Filtern landen in SQL Server oft in diesem Muster:
CREATE OR ALTER PROCEDURE dbo.SearchOrders
@CustomerID INT = NULL,
@Status NVARCHAR(20) = NULL
AS
SELECT o.OrderID, o.CustomerID, o.OrderDate, o.Status
FROM dbo.Orders o
WHERE (@CustomerID IS NULL OR o.CustomerID = @CustomerID)
AND (@Status IS NULL OR o.Status = @Status)Das Ergebnis stimmt immer. Der Optimierer muss aber jede Kombination aus gesetzten und leeren Parametern gleichzeitig abdecken. Welcher Plan im Cache landet, entscheidet der erste Aufruf. Ein Plan für die Suche nach einem Kunden wird dann auch für die Abfrage ohne Filter wiederverwendet, und die Lesezugriffe können sich um Größenordnungen unterscheiden. Das ist Parameter Sniffing. Es ist kein Fehler, sondern die Arbeitsweise des Plan-Caches. Einen Index, der das strukturell behebt, gibt es nicht; das Problem liegt in der Formulierung der Abfrage.
Wie Parameter Sniffing im Plan aussieht, mit echten Messwerten →
Die bekannten Auswege und ihr Preis
| Ansatz | Planqualität | Kompilierkosten | Lesbarkeit und Pflege |
|---|---|---|---|
| Catch-all-Abfrage | ein Plan für alle Formen | gering | bequem |
OPTION (RECOMPILE) | optimal je Aufruf | bei jedem Aufruf | bequem |
| Dynamisches SQL von Hand | optimal je Form, mit Wiederverwendung | gering | Zeichenketten, kein IntelliSense, Injection-Risiko bei Nachlässigkeit |
| Spezialisierte Einzelprozeduren | optimal je Muster | gering | viel doppelter Code |
| T-Lift | optimal je Form, mit Wiederverwendung | gering | Quelltext bleibt gültiges, debugbares T-SQL |
Wie T-Lift arbeitet
Sie schreiben Ihre Prozedur wie gewohnt und ergänzen Direktiven als Kommentare (--#). Weil es nur Kommentare sind, bleibt die Prozedur voll lauffähig: Sie entwickeln, testen und debuggen wie immer in SSMS. Wenn alles passt, rendert T-Lift daraus die dynamische Fassung.
--#[
SELECT o.OrderID, o.CustomerID, o.OrderDate, o.Status
FROM dbo.Orders o
WHERE --#if @CustomerID IS NOT NULL OR @Status IS NOT NULL
( --#-
@CustomerID IS NULL OR --#-
o.CustomerID = @CustomerID --#if @CustomerID IS NOT NULL
) --#-
AND --#if @CustomerID IS NOT NULL AND @Status IS NOT NULL
( --#-
@Status IS NULL OR --#-
o.Status = @Status --#if @Status IS NOT NULL
) --#-
--#]Das Ergebnis enthält nur noch die Bedingungen, die im jeweiligen Aufruf tatsächlich gebraucht werden. Jede Parameterkombination bekommt ihren eigenen, wiederverwendbaren Plan:
IF @CustomerID IS NOT NULL
SET @sql = @sql + 'o.CustomerID = @CustomerID' + CHAR(13)+CHAR(10)
...
EXEC sp_executesql @sql,
N'@CustomerID int, @Status nvarchar(20)', @CustomerID, @StatusWas darüber hinaus bemerkenswert ist
- Immer parametrisiert.
T-Lift erzeugtsp_executesqlmit einer korrekt typisierten Parameterliste aussys.parameters. Parameterwerte werden nie in die SQL-Zeichenkette eingebaut. Das ist sicherer als viel handgeschriebenes dynamisches SQL. - Buckets für den Plan-Cache.
Mit--#bucketsbekommt jeder Wertebereich eines Parameters einen eigenen Plan, etwa kleine gegenüber großen Beträgen. - Sicheres dynamisches ORDER BY.
--#sorterlaubt nur freigegebene Spalten. Die klassische Einfallstür für SQL-Injection beim Sortieren bleibt zu. - Hilfe für Altbestände.
@suggest = 1durchsucht bestehende Prozeduren nach Catch-all-Mustern und schlägt passende Annotationen vor. - Kein vergessenes Rendering.
Jede gerenderte Prozedur trägt einen Stempel mit Quell-Hash.@checkDriftmeldet, wenn die Quelle sich geändert hat und die gerenderte Fassung veraltet ist. - Prüfen vor dem Rendern.
@validateOnlyfindet offene Klammern, Tippfehler in Direktiven und Bedingungen außerhalb einer Sektion.
EXEC dbo.sp_tlift
@DatabaseName = 'IhreDatenbank',
@ProcedureName = 'AlteProzedur',
@suggest = 1; -- findet Catch-all-Muster und schlägt Annotationen vorGrenzen, die Sie kennen sollten
- Berechtigungen: Wie jedes dynamische SQL unterbricht T-Lift die Besitzverkettung. Benutzer brauchen Leserechte auf die Tabellen, oder die Prozedur läuft mit
EXECUTE AS OWNERoder per Modulsignatur. - Nicht immer schneller: T-Lift lohnt sich vor allem bei wiederkehrenden, häufig ausgeführten Abfrageformen, bei denen die Kompilierkosten von
OPTION (RECOMPILE)ins Gewicht fallen. Es ist kein Allheilmittel. - Mindestversion: SQL Server 2017, ohne CLR und ohne externe Abhängigkeiten.
Ausprobieren
Laden Sie sp_tlift.sql aus dem Repository und installieren Sie es in einer eigenen Hilfsdatenbank. EXEC dbo.sp_tlift @help = 1; erklärt alle Optionen. Das Repository enthält außerdem Demos mit realistischen Geschäftsabfragen und ein Skript, das T-Lift und OPTION (RECOMPILE) anhand von Query-Store-Daten vergleicht.
Als Nächstes geplant sind unter anderem Bucket-Grenzen aus Statistik-Histogrammen und eine Rückkopplung mit dem Query Store, die Plan-Regressionen gerenderter Prozeduren meldet. Fragen, Ideen und Beiträge sind über GitHub-Issues willkommen. Eine ausführlichere englische Einführung mit Hintergründen steht auf Medium.