![Understanding Stored Procedures in SQL]()
When we talk about backend performance, stored procedures play a major role in SQL Server.
They are precompiled, reusable, and secure β but if not written properly, they can become slow and inefficient.
In this article, weβll learn how to write high-performance stored procedures with simple explanations, tips, and an example flowchart.
π What is a Stored Procedure?
A stored procedure is a group of SQL statements stored in the database. Instead of sending multiple queries from your application, you can just call one stored procedure β making the process faster and easier to maintain.
Example
CREATE PROCEDURE usp_GetCustomerOrders
@CustomerID INT
AS
BEGIN
SELECT OrderID, OrderDate, TotalAmount
FROM Orders
WHERE CustomerID = @CustomerID;
END
You can execute it like:
EXEC usp_GetCustomerOrders @CustomerID = 1001;
π§ Why Performance Matters
When you work with large data β millions of rows β even small inefficiencies can slow everything down.
Optimizing your stored procedures can:
Reduce CPU and memory load
Improve response time
Make the system more scalable
Lower the chance of deadlocks or timeouts
βοΈ Best Practices for Writing High-Performance Stored Procedures
Letβs go step by step.
1. Use Proper SET Options
Start every stored procedure with:
SET NOCOUNT ON;
This stops the β(X rows affected)β message from returning, improving speed slightly and reducing unnecessary output.
2. Avoid SELECT *
Donβt select everything β it wastes memory and network bandwidth.
β Bad
SELECT * FROM Orders;
β
Good
SELECT OrderID, CustomerID, OrderDate FROM Orders;
3. Use Proper Indexing
Indexes are like shortcuts in a phone contact list. Without them, SQL Server must scan every row.
Example
CREATE INDEX IX_Orders_CustomerID ON Orders(CustomerID);
Always check query performance using Execution Plans in SSMS.
4. Avoid Cursors (If Possible)
Cursors process rows one by one β very slow for large data.
β Bad
DECLARE cur CURSOR FOR SELECT OrderID FROM Orders;
β
Good
Use set-based operations instead:
UPDATE Orders SET Status = 'Closed' WHERE OrderDate < '2024-01-01';
5. Use Temporary Tables or Table Variables Wisely
Temporary tables (#TempTable) can help when you have complex joins.
But donβt overuse them β they increase I/O on tempdb.
Use them only when needed:
SELECT * INTO #TempOrders FROM Orders WHERE Status = 'Pending';
6. Parameter Sniffing Control
Sometimes, SQL Server caches an execution plan based on the first parameter it sees, which may not work well for others.
To fix it
DECLARE @LocalCustomerID INT = @CustomerID;
SELECT * FROM Orders WHERE CustomerID = @LocalCustomerID;
This helps avoid performance issues due to parameter sniffing.
7. Use Transactions Carefully
Transactions ensure data consistency, but if they are too long, they can lock resources.
β
Use
BEGIN TRANSACTION;
-- Your statements
COMMIT TRANSACTION;
β Donβt keep a transaction open for long-running logic or unnecessary steps.
8. Use TRYβ¦CATCH for Error Handling
Itβs important for clean rollback and error logging.
Example
BEGIN TRY
BEGIN TRANSACTION;
UPDATE Accounts SET Balance = Balance - 100 WHERE ID = 1;
UPDATE Accounts SET Balance = Balance + 100 WHERE ID = 2;
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
EXEC spLogException ERROR_MESSAGE(), ERROR_LINE();
END CATCH;
9. Avoid Unnecessary Joins
Too many joins, especially on non-indexed columns, can slow performance drastically.
β
Keep joins only where needed, and always join on indexed keys.
10. Use Execution Plans and Statistics
Before finalizing your procedure:
SET STATISTICS IO ON;
SET STATISTICS TIME ON;
Then execute your procedure and check the output to understand how much time and I/O it consumes.
Also, view the execution plan (Ctrl + M in SSMS) to find table scans, missing indexes, or inefficient joins.
π Flowchart: Stored Procedure Execution Flow
Below is a simple flowchart to visualize how SQL Server executes a stored procedure:
ββββββββββββββββββββββββββ
β Client/Application β
β (calls procedure) β
ββββββββββββ¬βββββββββββββββ
β
βΌ
ββββββββββββββββββββββββββ
β SQL Server Receives β
β Stored Procedure Call β
ββββββββββββ¬βββββββββββββββ
β
βΌ
ββββββββββββββββββββββββββ
β Check Plan Cache β
β (Existing Execution?) β
ββββββββββββ¬βββββββββββββββ
Yes ββββββ€ βββββ No
βΌ βΌ
ββββββββββββββββββ ββββββββββββββββββ
β Use Cached Planβ β Generate New β
β (Fast) β β Execution Plan β
βββββββββ¬βββββββββ βββββββββ¬βββββββββ
β β
βΌ βΌ
ββββββββββββββββββββββββββ
β Execute SQL Commands β
ββββββββββββ¬βββββββββββββββ
β
βΌ
ββββββββββββββββββββββββββ
β Return Results/Errors β
β to Application β
ββββββββββββββββββββββββββ
This flow shows how SQL Server optimizes execution by reusing cached plans whenever possible.
π§© Example: Optimized Stored Procedure
Hereβs a real-world optimized procedure example:
CREATE PROCEDURE usp_GetActiveOrders
@StartDate DATE,
@EndDate DATE,
@CustomerID INT = NULL
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRY
SELECT O.OrderID,
O.OrderDate,
O.TotalAmount,
C.CustomerName
FROM Orders O
INNER JOIN Customers C ON O.CustomerID = C.ID
WHERE O.OrderDate BETWEEN @StartDate AND @EndDate
AND (@CustomerID IS NULL OR O.CustomerID = @CustomerID)
AND O.Status = 'Active'
ORDER BY O.OrderDate DESC;
END TRY
BEGIN CATCH
EXEC spLogException ERROR_MESSAGE(), ERROR_LINE();
END CATCH;
END
β
Key Points
π§ Quick Summary
| Best Practice | Description |
|---|
| SET NOCOUNT ON | Reduces overhead |
| Avoid SELECT * | Improves speed |
| Use Indexes | Faster lookups |
| Avoid Cursors | Use set-based logic |
| Handle NULLs | Prevent missing data |
| Use TRY/CATCH | Safe error handling |
| Manage Transactions | Keep short and clean |
| Review Execution Plan | Identify performance issues |
| Control Parameter Sniffing | Avoid bad caching |
| Avoid Unnecessary Joins | Keep it simple and fast |
β
Final Thoughts
Writing high-performance stored procedures isnβt about writing complex SQL β itβs about writing smart, clean, and efficient SQL.
Remember:
βA slow query written once can slow your application every single day.β
So, always think before you write:
If you keep these questions in mind, your stored procedures will run fast and stay maintainable for years.