Wenn SPs langsam sind – und keiner weiß warum

Du kennst das:
„Seit dem letzten Update ist alles langsamer.“
Und Du schaust in den Code der gespeicherten Prozedur – 400 Zeilen, null Kommentare.

SQL Server macht keinen Fehler. Aber manchmal eben nicht das, was Du willst.
Dann wird’s Zeit für eine fundierte Analyse und klare Optimierung.

Schritt 1: Langsame SPs finden

SELECT TOP 10 
    DB_NAME(qt.dbid) AS datenbank,
    qt.objectid,
    OBJECT_NAME(qt.objectid, qt.dbid) AS prozedur,
    qs.execution_count,
    qs.total_elapsed_time / qs.execution_count AS durchschnittszeit_ms,
    qs.total_logical_reads,
    qs.creation_time
FROM sys.dm_exec_procedure_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS qt
WHERE qs.database_id = DB_ID()
ORDER BY durchschnittszeit_ms DESC;

So findest Du die SPs, die pro Ausführung am meisten Zeit verbrauchen.
Nicht nach Bauchgefühl – sondern Daten.

Schritt 2: Ausführungspläne prüfen

SET SHOWPLAN_XML ON;
GO
EXEC dbo.deine_sp @parameter = 123;
GO
SET SHOWPLAN_XML OFF;

Wichtig:

  • Nested Loops bei vielen Zeilen → schlecht
  • „Table Scan“ bei großen Tabellen → schlecht
  • „Estimated Rows“ vs. „Actual Rows“ → abweichend? → Statistikproblem

Schritt 3: Parameter-Sniffing?

Wenn eine SP mal schnell, mal langsam ist – je nach Parameter – liegt’s meist am Parameter-Sniffing.

-- Beispielproblem
CREATE PROCEDURE dbo.kunden_abfragen
    @plz NVARCHAR(10)
AS
BEGIN
    SELECT * FROM dbo.kunden WHERE plz = @plz;
END;

SQL Server erstellt den Plan beim ersten Aufruf.
Der passt dann ggf. nicht für andere Werte.

Lösung 1: OPTION (RECOMPILE)

SELECT * FROM dbo.kunden WHERE plz = @plz
OPTION (RECOMPILE);

Erzeugt jedes Mal einen frischen Plan.
Hilft bei stark variierenden Datenverteilungen.

Lösung 2: Plan-Variante mit IF

IF @plz = '10115'
BEGIN
    -- Sonderfall mit anderem Plan
    SELECT * FROM dbo.kunden WHERE plz = '10115';
END
ELSE
BEGIN
    SELECT * FROM dbo.kunden WHERE plz = @plz;
END;

Kann grob, aber effektiv sein.

Schritt 4: SET-basierte Umstellung

-- Cursor? Nein danke.
DECLARE c CURSOR FOR SELECT id FROM kunden;

-- Stattdessen:
INSERT INTO zieltabelle (id)
SELECT id FROM kunden WHERE status = 'aktiv';

Cursors töten Performance.
SET-basiert ist fast immer besser.

Schritt 5: Unnötige Joins und Spalten raus

-- Statt:
SELECT * FROM kunden k
JOIN bestellungen b ON k.id = b.kunden_id
WHERE b.datum >= @datum;

-- Besser:
SELECT k.name, b.betrag
FROM kunden k
JOIN bestellungen b ON k.id = b.kunden_id
WHERE b.datum >= @datum;

Nur die Spalten, die Du brauchst.
Und keine Joins, die nur da sind „falls mal…“

Schritt 6: Statistik prüfen

UPDATE STATISTICS dbo.kunden WITH FULLSCAN;

Veraltete Statistiken → falsche Pläne → schlechte Performance.
Gerade bei stark veränderten Datenmengen.

Schritt 7: Indizes richtig nutzen

-- Beispielabfrage
SELECT * FROM bestellungen WHERE kunde_id = 123 AND status = 'offen';

-- Index-Empfehlung:
CREATE NONCLUSTERED INDEX ix_bestellungen_kunde_status
ON dbo.bestellungen(kunde_id, status)
INCLUDE (betrag, datum);

Ergebnis: Kein Scan, keine Sortierung, schneller Plan.

Mein Fazit

Gespeicherte Prozeduren sind oft über Jahre gewachsen.
Und dann bremst ein falscher Plan den ganzen Prozess.

Wenn Du SPs regelmäßig misst, prüfst und neu kompilierst,
vermeidest Du die klassischen Performance-Lecks.

Wenn Du willst, analysier ich Deine drei langsamsten Prozeduren.
Und sag Dir, wie man sie schnell bekommt – ohne sie komplett neu zu schreiben.

Tags:

No responses yet

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert