Intermediate SQL Server

T-SQL Stored Procedures Tutorial

Create reusable T-SQL stored procedures with parameters, variables, and error handling.

8 min read Tutorial

Stored procedures are named collections of T-SQL statements stored in the database. They encapsulate business logic, improve performance through plan caching, and provide a security layer between applications and tables. Stored procedures are one of the most powerful features in SQL Server.

Creating a Basic Stored Procedure

Use CREATE PROCEDURE (or CREATE PROC) to define a stored procedure. Execute it with EXEC.

CREATE PROCEDURE usp_GetActiveEmployees
AS
BEGIN
    SET NOCOUNT ON;

    SELECT EmployeeID, FirstName, LastName, Department
    FROM Employees
    WHERE IsActive = 1
    ORDER BY LastName;
END;

-- Execute the procedure
EXEC usp_GetActiveEmployees;

Input Parameters

Parameters make stored procedures flexible and reusable. You can assign default values so callers can omit optional parameters.

CREATE PROCEDURE usp_GetEmployeesByDepartment
    @Department NVARCHAR(50),
    @MinSalary DECIMAL(10,2) = 0  -- default value
AS
BEGIN
    SET NOCOUNT ON;

    SELECT FirstName, LastName, Salary
    FROM Employees
    WHERE Department = @Department
      AND Salary >= @MinSalary
    ORDER BY Salary DESC;
END;

-- Call with both parameters
EXEC usp_GetEmployeesByDepartment
    @Department = 'Engineering',
    @MinSalary = 80000;

-- Call with only required parameter
EXEC usp_GetEmployeesByDepartment @Department = 'Sales';

Output Parameters

Output parameters return values to the caller, which is useful when you need a computed result like a count or a generated ID.

CREATE PROCEDURE usp_CreateOrder
    @CustomerID INT,
    @Total DECIMAL(12,2),
    @NewOrderID BIGINT OUTPUT
AS
BEGIN
    SET NOCOUNT ON;

    INSERT INTO Orders (CustomerID, OrderDate, OrderTotal)
    VALUES (@CustomerID, GETDATE(), @Total);

    SET @NewOrderID = SCOPE_IDENTITY();
END;

-- Call and capture the output
DECLARE @OrderID BIGINT;
EXEC usp_CreateOrder
    @CustomerID = 42,
    @Total = 299.99,
    @NewOrderID = @OrderID OUTPUT;

SELECT @OrderID AS CreatedOrderID;

Variables and Control Flow

T-SQL supports local variables, IF/ELSE branching, and WHILE loops inside stored procedures.

CREATE PROCEDURE usp_ApplyDiscount
    @ProductID INT,
    @DiscountPercent DECIMAL(5,2)
AS
BEGIN
    SET NOCOUNT ON;
    DECLARE @CurrentPrice DECIMAL(10,2);

    SELECT @CurrentPrice = Price
    FROM Products WHERE ProductID = @ProductID;

    IF @CurrentPrice IS NULL
    BEGIN
        RAISERROR('Product not found.', 16, 1);
        RETURN;
    END

    UPDATE Products
    SET Price = @CurrentPrice * (1 - @DiscountPercent / 100)
    WHERE ProductID = @ProductID;
END;

Error Handling with TRY...CATCH

Wrap your logic in a TRY...CATCH block to handle errors gracefully. Use ERROR_MESSAGE() and related functions to capture error details.

CREATE PROCEDURE usp_TransferFunds
    @FromAccount INT,
    @ToAccount INT,
    @Amount DECIMAL(12,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
        IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;

        THROW;  -- re-raise the error
    END CATCH
END;

Stored procedures are a cornerstone of SQL Server development. They provide encapsulation, security through execute permissions, and performance benefits from cached execution plans. Use them to keep business logic close to your data.

Try this query in UnifySQL

Write, optimize, and collaborate on SQL Server queries with AI assistance.

Start Free