📦 SQL SERVER · ENTWICKLUNG

Stored Procedures – Das Kompendium

Von der ersten einfachen Prozedur bis zur professionellen Entwicklung mit Parametern, Fehlerbehandlung und sauberer Code-Struktur.

🎯 Zielgruppe: SQL-Entwickler, Datenbankentwickler 📅 Gültig für SQL Server 2012 – 2025

Creating Stored Procedures – Grundlagen

Syntax und erste Schritte

Eine Stored Procedure ist eine gespeicherte Sammlung von T-SQL-Anweisungen, die auf dem Server ausgeführt wird. Sie wird mit CREATE PROCEDURE (oder kurz CREATE PROC) erstellt und kann Parameter, Variablen, Kontrollfluss und Fehlerbehandlung enthalten.

-- Grundgerüst einer Stored Procedure
CREATE PROCEDURE schema_name.procedure_name
  @parameter1 datatype = default_value,
  @parameter2 datatype OUTPUT
AS
BEGIN
  SET NOCOUNT ON;
  -- SQL-Anweisungen
END;
💡 Best Practice: Verwenden Sie immer das Schema (z. B. dbo) und einen aussagekräftigen Namen. Nutzen Sie BEGIN...END für den Prozedurkörper.

Simple Stored Procedure

Das minimalistische Beispiel

Eine einfache Prozedur ohne Parameter, die Daten selektiert oder ändert.

-- Einfache Prozedur: Alle Kunden abrufen
CREATE PROCEDURE dbo.GetAllCustomers
AS
BEGIN
  SET NOCOUNT ON;
  SELECT CustomerID, Name, City FROM dbo.Customers ORDER BY Name;
END;
GO

-- Ausführen
EXEC dbo.GetAllCustomers;
Hinweis: Auch bei "einfachen" Prozeduren sollten Sie SET NOCOUNT ON verwenden, um die Anzahl der betroffenen Zeilen zu unterdrücken – das spart Netzwerkverkehr.

Input Parameters

Dynamische Prozeduren mit Werten von außen

Parameter werden nach dem Prozedurnamen in Klammern definiert. Sie können Standardwerte haben.

-- Prozedur mit Input-Parametern
CREATE PROCEDURE dbo.GetCustomersByCity
  @City NVARCHAR(50),
  @MinOrders INT = 0 -- Standardwert
AS
BEGIN
  SET NOCOUNT ON;
  SELECT CustomerID, Name, City, OrderCount
  FROM dbo.Customers
  WHERE City = @City AND OrderCount >= @MinOrders
  ORDER BY OrderCount DESC;
END;
GO

-- Ausführung mit Parameter
EXEC dbo.GetCustomersByCity @City = 'Berlin', @MinOrders = 5;
-- Oder mit Standardwert
EXEC dbo.GetCustomersByCity @City = 'Hamburg';
📌 Tipp: Verwenden Sie immer benannte Parameter beim Aufruf – das macht den Code lesbarer und weniger fehleranfällig.

Output Parameters

Werte aus der Prozedur zurückgeben

Mit dem Schlüsselwort OUTPUT können Sie Werte aus der Prozedur an den Aufrufer zurückgeben – ideal für Statusmeldungen, berechnete Werte oder Primärschlüssel.

-- Prozedur mit Output-Parameter
CREATE PROCEDURE dbo.InsertOrder
  @CustomerID INT,
  @OrderDate DATE,
  @NewOrderID INT OUTPUT
AS
BEGIN
  SET NOCOUNT ON;
  INSERT INTO dbo.Orders (CustomerID, OrderDate)
  VALUES (@CustomerID, @OrderDate);
  
  SET @NewOrderID = SCOPE_IDENTITY();
END;
GO

-- Aufruf mit Output-Variable
DECLARE @OrderID INT;
EXEC dbo.InsertOrder @CustomerID = 123, @OrderDate = '2026-05-08', @NewOrderID = @OrderID OUTPUT;
SELECT @OrderID AS NeueBestellung;
✅ Merke: Output-Parameter sind effizienter als ein einzelnes Resultset, wenn nur ein Wert zurückgegeben werden soll.

Try … Catch – Robuste Fehlerbehandlung

Transaktionen sicher machen

Ab SQL Server 2005 können Sie Fehler mit BEGIN TRY...BEGIN CATCH abfangen und gezielt behandeln – unerlässlich für fehlertoleranten Code.

CREATE PROCEDURE dbo.TransferMoney
  @FromAccount INT,
  @ToAccount INT,
  @Amount DECIMAL(18,2)
AS
BEGIN
  SET NOCOUNT ON;
  BEGIN TRY
    BEGIN TRANSACTION;
    
    UPDATE Accounts SET Balance = Balance - @Amount WHERE AccountID = @FromAccount;
    UPDATE Accounts SET Balance = Balance + @Amount WHERE AccountID = @ToAccount;
    
    COMMIT TRANSACTION;
  END TRY
  BEGIN CATCH
    ROLLBACK TRANSACTION;
    
    -- Fehlerinformationen zurückgeben
    DECLARE @ErrorMessage NVARCHAR(4000) = ERROR_MESSAGE();
    DECLARE @ErrorSeverity INT = ERROR_SEVERITY();
    DECLARE @ErrorState INT = ERROR_STATE();
    
    RAISERROR(@ErrorMessage, @ErrorSeverity, @ErrorState);
  END CATCH
END;
Nützliche Fehlerfunktionen im CATCH-Block:
ERROR_NUMBER(), ERROR_MESSAGE(), ERROR_SEVERITY(), ERROR_STATE(), ERROR_LINE(), ERROR_PROCEDURE().

Commenting Code – Dokumentation im Code

Warum Kommentare den Unterschied machen

SQL unterstützt einzeilige (--) und mehrzeilige (/*...*/) Kommentare. Gute Kommentare erklären das "Warum", nicht das "Was".

CREATE PROCEDURE dbo.CleanupOldRecords
  @RetentionDays INT
AS
BEGIN
  SET NOCOUNT ON;
  
  -- Lösche alle Datensätze, die älter als @RetentionDays sind
  -- Die Tabelle ist nach CreateDate partitioniert, daher effizient
  DELETE FROM dbo.AuditLog
  WHERE CreateDate < DATEADD(DAY, -@RetentionDays, GETDATE());
  
  /*
    Mehrzeiliger Kommentar:
    Nach dem Löschen wird der Index neu organisiert.
    Dies geschieht nur, wenn mehr als 1000 Zeilen betroffen waren.
  */
  IF @@ROWCOUNT > 1000
    ALTER INDEX IX_AuditLog_CreateDate ON dbo.AuditLog REORGANIZE;
END;
Wichtige Regel: Kommentieren Sie nicht offensichtliches (z. B. "Inkrementiere Zähler"). Erklären Sie Fachlogik, Annahmen oder Hintergründe. Veraltete Kommentare sind schlimmer als keine.

Naming Conventions – Einheitliche Benennung

Lesbarkeit und Wartbarkeit steigern

Empfohlene Konventionen

  • Präfix: usp_ oder sp_? Vorsicht: sp_ ist systemreserviert – besser usp_ (User Stored Procedure) oder gar kein Präfix.
  • PascalCase verwenden: GetCustomerOrders statt get_customer_orders (konsistent mit systemgespeicherten Prozeduren).
  • Schema immer angeben: dbo.usp_GetCustomers – vermeidet Namensauflösungsprobleme.
  • Parameter: @CustomerID, @OrderDateFrom (camelCase mit @).
  • Output-Parameter: Endung Out oder Output (z. B. @NewIDOut).

Beispiele für gute Namen

  • usp_GetProductsByCategory
  • usp_InsertOrder
  • usp_UpdateEmployeeSalary
  • usp_CalculateInvoiceTotal
  • usp_DeleteInactiveCustomers

SET NOCOUNT ON – Warum diese Zeile so wichtig ist

Leistungsgewinn durch Unterdrückung von Rowcount-Meldungen

SET NOCOUNT ON verhindert, dass SQL Server für jede betroffene Zeile die Meldung "(x Zeile(n) betroffen)" an den Client sendet. Das reduziert den Netzwerkverkehr und kann die Leistung erheblich verbessern – besonders bei Schleifen oder vielen Updates.

CREATE PROCEDURE dbo.BulkUpdate
AS
BEGIN
  SET NOCOUNT ON; -- Unbedingt am Anfang
  
  UPDATE dbo.LargeTable SET Status = 'Processed' WHERE Status = 'New';
  -- Ohne SET NOCOUNT ON würde hier eine Meldung über Hunderttausende Zeilen gesendet
  
  DELETE FROM dbo.Logs WHERE LogDate < '2020-01-01';
  -- Auch hier: Keine Rückmeldung über gelöschte Zeilen an den Client
END;
✅ Best Practice: Setzen Sie SET NOCOUNT ON am Anfang jeder Stored Procedure. Nur wenn Sie die Anzahl der betroffenen Zeilen in einer Anwendung auswerten müssen, lassen Sie es ausnahmsweise weg.

DROP PROCEDURE – Entfernen einer Prozedur

Löschen mit Vorsicht

Mit DROP PROCEDURE (oder kurz DROP PROC) wird eine gespeicherte Prozedur vollständig aus der Datenbank entfernt. Das Löschen ist nicht rückgängig zu machen.

-- Einzelne Prozedur löschen
DROP PROCEDURE dbo.GetAllCustomers;

-- Mehrere Prozeduren mit einer Anweisung
DROP PROCEDURE dbo.GetAllCustomers, dbo.InsertOrder;

-- Prüfen, ob Prozedur existiert (sicheres Löschen)
IF OBJECT_ID('dbo.GetAllCustomers', 'P') IS NOT NULL
  DROP PROCEDURE dbo.GetAllCustomers;
⚠️ Achtung: Vor dem Löschen einer Prozedur sollten Sie abhängige Objekte (andere Prozeduren, Funktionen, Trigger) prüfen, die sie möglicherweise aufrufen. Verwenden Sie sp_depends oder die Abhängigkeitsansichten.

ALTER PROCEDURE – Ändern ohne Verlust von Berechtigungen

Der sicherere Weg, Prozeduren zu aktualisieren

ALTER PROCEDURE ändert die Definition einer vorhandenen Prozedur, behält aber bestehende Berechtigungen (GRANT/DENY) bei. Das ist der große Vorteil gegenüber DROP/CREATE.

-- Vorhandene Prozedur ändern
ALTER PROCEDURE dbo.GetCustomersByCity
  @City NVARCHAR(50),
  @MinOrders INT = 0,
  @IncludeInactive BIT = 0 -- Neuer Parameter
AS
BEGIN
  SET NOCOUNT ON;
  SELECT CustomerID, Name, City, OrderCount
  FROM dbo.Customers
  WHERE City = @City 
    AND OrderCount >= @MinOrders
    AND (IsActive = 1 OR @IncludeInactive = 1)
  ORDER BY OrderCount DESC;
END;
📌 Tipp: Verwenden Sie in Ihren Migrationsskripten immer ALTER für bestehende Prozeduren, es sei denn, Sie wollen bewusst alle Berechtigungen zurücksetzen. Mit ALTER bleiben auch die SQL-Agent-Jobs, die die Prozedur aufrufen, funktionsfähig.

Vollständiges Beispiel – Best Practices vereint

Eine Prozedur mit allem Drum und Dran

Diese Beispielprozedur zeigt eine professionelle Umsetzung mit Parametern, Standardwerten, Fehlerbehandlung, Transaktion, NOCOUNT und aussagekräftigen Kommentaren.

CREATE PROCEDURE dbo.usp_UpdateProductStock
  @ProductID INT,
  @QuantityChange INT,
  @NewStockOut INT = NULL OUTPUT,
  @Success BIT OUTPUT
AS
BEGIN
  SET NOCOUNT ON;
  
  BEGIN TRY
    BEGIN TRANSACTION;
    
    -- Aktuellen Bestand ermitteln
    DECLARE @CurrentStock INT;
    SELECT @CurrentStock = StockQuantity
    FROM dbo.Products
    WHERE ProductID = @ProductID;
    
    -- Prüfen, ob Produkt existiert
    IF @CurrentStock IS NULL
    BEGIN
      SET @Success = 0;
      RAISERROR('Produkt %d nicht gefunden.', 16, 1, @ProductID);
    END
    
    -- Neuen Bestand berechnen
    UPDATE dbo.Products
    SET StockQuantity = StockQuantity + @QuantityChange,
      LastModified = GETDATE()
    WHERE ProductID = @ProductID;
    
    -- Output-Parameter füllen
    SELECT @NewStockOut = StockQuantity
    FROM dbo.Products
    WHERE ProductID = @ProductID;
    
    SET @Success = 1;
    COMMIT TRANSACTION;
  END TRY
  BEGIN CATCH
    ROLLBACK TRANSACTION;
    SET @Success = 0;
    SET @NewStockOut = NULL;
    
    -- Fehler protokollieren (in eigene Log-Tabelle)
    INSERT INTO dbo.ErrorLog (ProcedureName, ErrorMessage, ErrorLine, ErrorDate)
    VALUES ('usp_UpdateProductStock', ERROR_MESSAGE(), ERROR_LINE(), GETDATE());
    
    -- Fehler erneut auslösen (für Aufrufer)
    THROW;
  END CATCH
END;

Systemansichten – Überblick über alle Prozeduren

-- Alle benutzerdefinierten Prozeduren anzeigen
SELECT 
  SCHEMA_NAME(schema_id) AS SchemaName,
  name AS ProcedureName,
  create_date,
  modify_date
FROM sys.procedures
WHERE is_ms_shipped = 0
ORDER BY SchemaName, name;

-- Prozedur-Code anzeigen
EXEC sp_helptext 'dbo.usp_UpdateProductStock';

-- Abhängigkeiten einer Prozedur finden
SELECT referencing_schema_name, referencing_entity_name
FROM sys.dm_sql_referencing_entities('dbo.usp_UpdateProductStock', 'OBJECT');
Alle Beispiele getestet unter SQL Server 2016 – 2025 | Weitere Tipps unter dtc-sql.de

Cookies user preferences
We use cookies to ensure you to get the best experience on our website. If you decline the use of cookies, this website may not function as expected.
Accept all
Decline all
Analytics
Tools used to analyze the data to measure the effectiveness of a website and to understand how it works.
Google Analytics
Advertisement
If you accept, the ads on the page will be adapted to your preferences.
Google Ad
Save