Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / SQL

Stored Procedures Best Practices

3.89/5 (6 votes)
30 Oct 2014CPOL6 min read 66.7K  
Stored procedures best practices

What is a Stored Procedure (SP)

The stored procedure is a set of PL/SQL statements written inside a named block. We use stored procedures in SQL Server to perform the very basic operations on database tables.

Advantages

  • Stored Procedures are very useful in multi development environment as they serve developers or applications from a single point.
  • They are precompiled and the execution of the stored procedures is very fast when compared to SQL queries
  • They sit inside the database and execute from it. The changes made in one place are visible to everybody who is accessing the stored procedures.

Stored Procedure Syntax

SQL
CREATE PROCEDURE <Procedure_Name, sysname, ProcedureName> 
-- Add the parameters for the stored procedure here
<@Param1, sysname, @p1> <Datatype_For_Param1, , int> = <Default_Value_For_Param1, , 0>, 
<@Param2, sysname, @p2> <Datatype_For_Param2, , int> = <Default_Value_For_Param2, , 0>
AS
BEGIN
   -- SET NOCOUNT ON added to prevent extra result sets from
   -- interfering with SELECT statements.
   SET NOCOUNT ON;
   -- Insert statements for procedure here
   SELECT <@Param1, sysname, @p1>, <@Param2, sysname, @p2>
END
GO

The syntax is very clear. It starts with the command CREATE PROCEDURE followed by procedure name and the list of parameters that can be passed to it with datatypes and default values. The parameters declared here must be passed from the context from where the procedure is getting executed. If nothing is passed for a parameter and any default value assigned to it, then the default value will be taken and the procedure executes. If no default value is assigned, then it will throw an error like 'Value does not supplied for <Parameter_1>'.

After the parameter declaration, we will begin the actual body of the procedure and start writing the business logic and SQL queries for the processing.

Stored Procedures Best Practices

So far, we saw the syntax and parameter declaration for the stored procedure. Now, this is the time to see how we can write stored procedures for a particular table. What are the best practices to do it?

For any database table, the primary thing or primary operations that we can implement are CRUD operations.

  • C - Create / Insert
  • R - Read / Select
  • U - Update / Edit
  • D - Delete / Remove

These are the basic operations, that every database programmer should implement for every table. The industry standards or the best practices followed by developers are as follows:

Before getting into the practices, first create a table with the following statement on your database.

Create Table Script

SQL
CREATE TABLE [dbo].[Customer](
	[CustomerID] [int] IDENTITY(1,1) NOT NULL,
	[CustomerName] [varchar](50) NULL,
	[YTDOrders] [int] NULL,
	[YTDSales] [int] NULL,
	 CONSTRAINT [PK_Customer] PRIMARY KEY CLUSTERED 
	(
		[CustomerID] ASC
	)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, 
           ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

The table is created as CustomerID as integer, identity column and primary key. So whatever operations we perform, the primary key is important to perform different kinds of operations like update, select and delete.

Stored Procedure for UPSERT

Don't get frightened by this new word UPSERT, this is the name used by DB programmers for stored procedures which perform both insert + update. So the very first step we should consider for our table is to write a procedure which performs both insert and update operations as below.

Procedure Script

SQL
CREATE PROCEDURE Customer_Upsert
@CustomerID int=null,
@CustomerName nvarchar(40)=null,
@YTDOrders int=null,
@YTDSales int=null
AS
BEGIN
	-- SET NOCOUNT ON added to prevent extra result sets from
	-- interfering with SELECT statements.
	SET NOCOUNT ON;

   if(@CustomerID is null)
	INSERT INTO Customer(CustomerName,YTDOrders, YTDSales) Values(@CustomerName,@YTDOrders,@YTDSales)
   else
	UPDATE Customer SET
	CustomerName=coalesce(@CustomerName, CustomerName),
	YTDOrders=coalesce(@YTDOrders,YTDOrders),
	YTDSales=coalesce(@YTDSales,YTDSales)
	WHERE CustomerID=@CustomerID
END
GO

Here, we are passing all the columns values as parameters and we are assigning them with their respective default values. The advantage of assigning default values is when application developers are working with this procedure and they forgot to pass few parameter values, then it should break. And the default value assignment is very useful when we are performing some search kind of filtering and on update operations.

Here, we are checking whether the @CustomerID is passed or not? For new records, as they won't have any primary column value, it will be passed empty and our stored procedure will identify it as new records and the insertion statement executes.

Try with this sample statement:

SQL
EXEC Customer_Upsert @CustomerName='Insert SP',@YTDOrders='13',@YTDSales='4'

And now, execute the select statement to see the records in the table.

Result after Upsert Stored Procedure Performed Insert
Result after Upsert Stored Procedure Performed Insert

If the @CustomerID is passed, our Stored Procedure will identify as it is already exists and executes the Update statement accordingly.

Here, the interesting point we should see is COALESCE, this function is very very useful while updating. Suppose consider in some scenario, I have updated only few column values for a customer. I want to update the sales of a customer, then I will pass only the new value for sales what about other column values if I don't pass their values if I do not use COALESCE, I will end up in some permutations and combinations as I need to write conditions like if only customer name passed or customer name plus sales like this. Now COALESCE solves my problem and provides a simple single line statement to finish my job.

What it will do is it checks whether the passed value is null or not. If it is null, it will assign the old value to it, otherwise the new value. 

Try with this simple SQL Statement:

SQL
EXEC Customer_Upsert @CustomerID=1,@YTDSales='10' 
Result after upsert stored procedure executes update
Result after upsert stored procedure executes update

Now see the result and compare with the old result select after insert.

This is how we one should write Upsert stored procedure, which will provide two kinds of functionality to insert and update.

Stored Procedure For Delete

This is a very simple procedure to do, and requires only primary key or any combination of columns. But the delete is of 2 kinds.

Procedure Script
SQL
USE [SampleDAC]
GO

/****** Object:  StoredProcedure [dbo].[Customer_Delete]    Script Date: 01/08/2013 23:28:39 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE PROCEDURE [dbo].[Customer_Delete]
@CustomerID int,
@DeletePermanent bit
AS
BEGIN

SET NOCOUNT ON;

IF @DeletePermanent = 1
DELETE FROM Customer WHERE CustomerID=@CustomerID
ELSE
UPDATE Customer SET IsActive=0 where CustomerID=@CustomerID

END

GO 
1. Permanent Delete

Here, we delete entire row for a passed primary key. The above stored procedure has the section IF to deal with this functionality:

SQL
IF @DeletePermanent = 1
DELETE FROM Customer WHERE CustomerID=@CustomerID

Example:

SQL
EXEC dbo.Customer_Delete 3,1
SELECT * FROM Customer
1 Insert SP 0 10 1
2 update sp 20 23 0
4 select sp 20 23 1
5 select sp1 21 24 1

This shows the results it deleted the records number 3 permanently from the database.

2. Soft Delete

Here, we will make a row as disabled by means of maintaining a bit column for that table. If the bit value is 1, it is active otherwise it is disabled.

Now to try this, add a new column of type bit and name it as IsActive to customers table.

SQL
ALTER TABLE Customer ADD IsActive BIT

The above stored procedure has the section ELSE which deals with the soft delete which means it will update the records flag as "0" to indicate that this record is not in use or disabled.

SQL
ELSE
UPDATE Customer SET IsActive=0 where CustomerID=@CustomerID

Example:

SQL
EXEC dbo.Customer_Delete 2,0
SELECT * FROM Customer 
1 Insert SP 0 10 1
2 update sp 20 23 0
4 select sp 20 23 1
5 select sp1 21 24 1

If you see the above results, it is clear that row number 2 IsActive flag has been set to 0 means the record is no more in use or it is disabled, our queries should be written to check this flag.

Stored Procedure For Reader

This is the final and last part which plays a very important role in the data accessing, fetching or reading part of any database table.

Procedure Script

SQL
CREATE PROCEDURE [dbo].[Customer_Reader]
	@CustomerID int=null,
	@CustomerName varchar(50)=null,
	@IsActive bit = null
AS
BEGIN

	SET NOCOUNT ON;

	SELECT * FROM Customer
	WHERE
	(CustomerID=@CustomerID OR @CustomerID IS NULL)
	AND (CustomerName=@CustomerName OR @CustomerName IS NULL)
	AND (IsActive =@IsActive OR @IsActive IS NULL)

END
GO

This is a simple script and we are declaring all the parameters as optional which means you can pass combination of these or none to the procedure. If you don't pass any parameter, it will return all the records from table. Or if you pass any combination of these parameters, it means you can pass any one, two or three of them according to your parameter filteration, the result will come.

How It Works

This is pretty simple as we all know truth table for OR & AND as they are like below. We can easily identify the results of it.

Just check if I don't pass any parameter to this procedure what will happen:

SQL
CustomerID=@CustomerID i.e. False
@CustomerID IS NULL i.e. True
False or True i.e. True

Like this, others mean all filters returns true and And operation returns true and it returns all the records from the table.

Truth table for And Operation
Truth table for And Operation
Truth table for OR Operation
Truth table for OR Operation

Likewise, whichever parameter you pass for that if the value is true and the others by default, they return true because they are null you will get exact result.

This is how most of the search operations on websites are implemented.

For example:

SQL
EXEC dbo.Customer_Reader
1 Insert SP 0 10 1
2 update sp 20 23 0
4 select sp 20 23 1
5 select sp1 21 24 1
SQL
EXEC dbo.Customer_Reader @CustomerID=1
1 Insert SP 0 10 1
SQL
EXEC dbo.Customer_Reader @IsActive=1
1 Insert SP 0 10 1
4 select sp 20 23 1
5 select sp1 21 24 1

This is how we should practice with the stored procedures while writing. I hope this tutorial helps you.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)