DEV Community

Cover image for Beginner’s Guide to SQL Server Stored Procedures
Michael Gokey
Michael Gokey

Posted on

Beginner’s Guide to SQL Server Stored Procedures

Imagine you're new to a software team.
You notice the same chunk of database code (SQL) copied and pasted in five different places.
Your lead developer says, "Hey, we should make this a 'stored procedure' instead."" You nod confidently, but inside you're thinking, "What exactly is a stored procedure, and why does everyone keep talking about them?" If you're a new developer or DBA who's heard the term thrown around but never quite understood the hype, you're in the right place. Let's demystify stored procedures and show you why they're one of the most powerful tools in your database toolkit.

TL;DR - The Quick Facts

What: Reusable SQL code blocks stored in the database run faster than regular SQL
Why: Better performance, improved security, and cleaner application code
When: Use for complex business logic, frequently-used queries, and data validation operations

What are Stored Procedures?

A stored procedure is essentially a named group of SQL statements that you can execute as a single unit. Instead of your program sending many separate database commands, it can just ask the stored procedure to do all the work.
It's like creating a shortcut. Instead of giving someone detailed instructions for "Get the milk" every time, you just say "Get Milk" and they know all the steps involved. SQL Server saves and optimizes your procedure the first time it runs, so it executes faster with repeated calls.

Prerequisites

Before diving in, make sure you're comfortable with:
Basic SQL SELECT, INSERT, UPDATE, DELETE statements
Understanding of database tables and relationships
Basic knowledge of SQL Server Management Studio (SSMS) or similar database tools

Copy-Paste Starter Template

-- Template: Basic Stored Procedure with Error Handling
CREATE PROCEDURE [dbo].[YourProcedureName]
    @Parameter1 VARCHAR(50),        -- Input parameter
    @Parameter2 INT = 0,            -- Input parameter with default value
    @OutputParameter INT OUTPUT     -- Output parameter (optional)
AS
BEGIN
    -- Prevent extra result sets from interfering with SELECT statements
    SET NOCOUNT ON;

    BEGIN TRY
        -- Your main logic goes here
        SELECT 'Replace this with your actual SQL logic' AS Message

        -- Set output parameter if using one
        SET @OutputParameter = @@ROWCOUNT

    END TRY
    BEGIN CATCH
        -- Handle errors gracefully
        PRINT 'Error occurred: ' + ERROR_MESSAGE()
        RETURN -1  -- Return error code
    END CATCH
END
Enter fullscreen mode Exit fullscreen mode

Practical Examples

Example 1: Basic Procedure (No Parameters)

CREATE PROCEDURE GetAllEmployees
AS
BEGIN
    SET NOCOUNT ON;

    SELECT EmployeeID, FirstName, LastName, Department
    FROM Employees
    ORDER BY LastName, FirstName
END
Enter fullscreen mode Exit fullscreen mode

To execute it:

EXEC GetAllEmployees
Enter fullscreen mode Exit fullscreen mode

Example 2: Procedure with Input Parameters

CREATE PROCEDURE GetEmployeesByDepartment
    @DepartmentName VARCHAR(50)
AS
BEGIN
    SET NOCOUNT ON;

    -- Validate input
    IF @DepartmentName IS NULL OR @DepartmentName = ''
    BEGIN
        PRINT 'Department name cannot be empty'
        RETURN -1
    END

    SELECT EmployeeID, FirstName, LastName, Salary
    FROM Employees
    WHERE Department = @DepartmentName
    ORDER BY LastName
END
Enter fullscreen mode Exit fullscreen mode

To execute it:

EXEC GetEmployeesByDepartment @DepartmentName = 'Sales'
Enter fullscreen mode Exit fullscreen mode

Example 3: Procedure with Input and Output Parameters

CREATE PROCEDURE GetEmployeeCount
    @DepartmentName VARCHAR(50),
    @EmployeeCount INT OUTPUT
AS
BEGIN
    SET NOCOUNT ON;

    SELECT @EmployeeCount = COUNT(*)
    FROM Employees
    WHERE Department = @DepartmentName OR @DepartmentName IS NULL
END
Enter fullscreen mode Exit fullscreen mode

To execute it:

DECLARE @Count INT
EXEC GetEmployeeCount @DepartmentName = 'Marketing', @EmployeeCount = @Count OUTPUT
SELECT @Count AS TotalEmployees
Enter fullscreen mode Exit fullscreen mode

Example 4: Complex Business Logic with Transaction

CREATE PROCEDURE ProcessEmployeeRaise
    @EmployeeID INT,
    @RaisePercentage DECIMAL(5,2)
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @CurrentSalary DECIMAL(10,2)
    DECLARE @NewSalary DECIMAL(10,2)

    BEGIN TRY
        BEGIN TRANSACTION

        -- Validate inputs
        IF @RaisePercentage <= 0 OR @RaisePercentage > 50
        BEGIN
            RAISERROR('Raise percentage must be between 0 and 50', 16, 1)
            RETURN -1
        END

        -- Get current salary
        SELECT @CurrentSalary = Salary
        FROM Employees
        WHERE EmployeeID = @EmployeeID

        IF @CurrentSalary IS NULL
        BEGIN
            RAISERROR('Employee not found', 16, 1)
            RETURN -1
        END

        -- Calculate new salary
        SET @NewSalary = @CurrentSalary * (1 + @RaisePercentage / 100)

        -- Update the salary
        UPDATE Employees
        SET Salary = @NewSalary,
            LastModified = GETDATE()
        WHERE EmployeeID = @EmployeeID

        -- Log the change
        INSERT INTO SalaryHistory (EmployeeID, OldSalary, NewSalary, ChangeDate)
        VALUES (@EmployeeID, @CurrentSalary, @NewSalary, GETDATE())

        COMMIT TRANSACTION

        -- Return success message
        SELECT 'Salary updated successfully' AS Result,
               @CurrentSalary AS OldSalary,
               @NewSalary AS NewSalary

    END TRY
    BEGIN CATCH
        ROLLBACK TRANSACTION
        PRINT 'Error processing raise: ' + ERROR_MESSAGE()
        RETURN -1
    END CATCH
END
Enter fullscreen mode Exit fullscreen mode

Performance Comparison: Before and After

-- Without stored procedure (slower)
SELECT COUNT(*) FROM Orders WHERE CustomerID = 123 AND OrderDate > '2024-01-01'
SELECT AVG(TotalAmount) FROM Orders WHERE CustomerID = 123 AND OrderDate > '2024-01-01'
Enter fullscreen mode Exit fullscreen mode

Create this stored procedure:

-- With stored procedure (faster)
CREATE PROCEDURE GetCustomerOrderStats
    @CustomerID INT,
    @StartDate DATE
AS
BEGIN
    SET NOCOUNT ON;

    SELECT 
        COUNT(*) AS OrderCount,
        AVG(TotalAmount) AS AverageAmount,
        SUM(TotalAmount) AS TotalAmount
    FROM Orders 
    WHERE CustomerID = @CustomerID AND OrderDate > @StartDate
END
Enter fullscreen mode Exit fullscreen mode

The stored procedure version will be faster because SQL Server compiles and caches the execution plan, plus it reduces network traffic by sending one call instead of multiple queries.

Testing Your Stored Procedures

Always test your procedures step-by-step:

  1. Test with valid data first
EXEC GetEmployeesByDepartment @DepartmentName = 'Sales'
Enter fullscreen mode Exit fullscreen mode
  1. Test edge cases
-- Test with NULL
EXEC GetEmployeesByDepartment @DepartmentName = NULL

-- Test with empty string
EXEC GetEmployeesByDepartment @DepartmentName = ''

-- Test with non-existent department
EXEC GetEmployeesByDepartment @DepartmentName = 'Unicorn Department'
Enter fullscreen mode Exit fullscreen mode
  1. Test error conditions
-- Test with invalid parameters
EXEC ProcessEmployeeRaise @EmployeeID = 999999, @RaisePercentage = 100
Enter fullscreen mode Exit fullscreen mode

Debugging Stored Procedures

When your procedure isn't working:

  1. Add PRINT statements to see what's happening:
PRINT 'Current salary: ' + CAST(@CurrentSalary AS VARCHAR(20))
Enter fullscreen mode Exit fullscreen mode
  1. Use SELECT statements to check variable values:
SELECT @CurrentSalary AS CurrentSalary, @NewSalary AS NewSalary
Enter fullscreen mode Exit fullscreen mode
  1. Run sections individually - comment out parts of your procedure and test smaller pieces

Key Benefits

Performance: SQL Server saves and optimizes your procedure, making repeat calls faster than sending the same SQL over and over.

Security: Procedures help prevent SQL injection attacks and let you grant execute permissions without giving direct table access.

Maintainability: Business logic lives in one place in the database, making updates easier without changing application code.

Network Efficiency: Send one procedure call instead of multiple SQL statements.

Real Talk: When NOT to Use Stored Procedures

Simple CRUD Operations: If you're just doing basic INSERT/UPDATE/DELETE on single tables, stored procedures might be overkill.

Microservices Architecture: When each service manages its own data, stored procedures can create tight coupling between services.

ORM-Heavy Applications: If your application uses Entity Framework or similar ORMs effectively, stored procedures might complicate your data layer.

Testing Complexity: Stored procedures can make unit testing more difficult since they're harder to mock.

Version Control Challenges: Database objects are trickier to version control than application code.

Version Control Tips for Teams

  • Always script your procedures: Use SSMS to generate CREATE scripts
  • Use ALTER instead of DROP/CREATE when modifying existing procedures
  • Include rollback scripts for procedure changes
  • Document parameter changes clearly in your commit messages

Common Error Messages and Solutions

"Procedure 'X' expects parameter 'Y', which was not supplied"

  • Solution: Check your parameter names and make sure you're passing all required parameters

"Invalid object name 'TableName'"

  • Solution: Verify the table exists and you have permissions to access it

"String or binary data would be truncated"

  • Solution: Check your parameter sizes match the data you're passing

Where Stored Procedures Run

Stored procedures are specific to Microsoft SQL Server. Other database systems have similar concepts:

  • MySQL: Also calls them stored procedures
  • PostgreSQL: Has stored procedures and functions
  • Oracle: Uses PL/SQL procedures and functions
  • SQLite: Doesn't support stored procedures

Try This Now

  1. Pick a common query
  2. Turn it into a stored procedure
  3. Add validation
  4. Test with various inputs
  5. Compare execution time

Next Steps

As you get comfortable with basic stored procedures, explore the following:

  • Stored functions (return single values)
  • Table-valued functions (return result sets)
  • Triggers (procedures that run automatically)
  • Dynamic SQL within stored procedures (advanced topic)
  • Recursive procedures for hierarchical data

Remember: the best way to learn stored procedures is by writing them. Start simple, test thoroughly, and gradually add complexity as your confidence grows.

Happy coding, and welcome to the world of stored procedures!

Top comments (0)