You are on page 1of 37

COMMONWEALTH OF PENNSYLVANIA

DEPARTMENT OF PUBLIC WELFARE


INFORMATION TECHNOLOGY STANDARD
Name Of Standard: MS SQL Server
2008 Naming and Coding Standard

Number:
STD-DMS009

Domain:

Category:
Data

Date Issued:

Issued By Direction Of:


06/20/2012

Date Revised:
James Weaver, Dir of Div of Tech Engineering
Abstract:
The purpose of this document is to detail and give examples of Microsoft Structured Query
Language (MS SQL) development standards followed at Department of Public Welfare (DPW).
General:
Coding standards are conventions and methods developers follow when developing, editing or
maintaining program code. Better programming style, developer understanding, readability and
reduced application development time are the results of following coding standards.
Database Modeling Environment
CA AllFusion ERwin Data Modeler is the standard tool for designing and modeling SQL Server
2008 databases.
All models will be reviewed by Database Management Section.
All completed models will be stored in the model repository.
Database Data Definition Environment
Microsoft SQL Server Management Studio is the standard tool to use for the execution and
modification of Data Definition Language (DDL) for the development of SQL Server 2008
databases. The general process is:
1. Initially generate a DDL from ERwin using forward engineering, or if required, create
DDL statements manually within Management Studio

MS SQL Server 2008 Naming and Coding Standard.doc

Page 1 of 37

2. Execute the DDL within Management Studio and, if necessary, edit and/or tune the
statements using the Management Studio environment
3. Reverse engineer the database back into ERwin to keep the two synchronized
Store revised data models and all DDL scripts within Visual Source Safe or Team Foundation
Server. The DDL scripts will be stored in a consistent manner under the project folder within
Visual Source Safe or Team Foundation Server.
Database Programming Environment
Use SQL Server 2008 Management Studio to code and test stored procedures and triggers.
Prototype every process using t-sql within SQL Server Management Studio prior to creating a
new stored procedure, view, function or trigger. This will result in ease of debugging.
Use Execution Plans
Estimated Execution plans should be used while developing T-SQL
Actual Execution plans need to be included in the migration packet
Use Database Engine Tuning Advisor
SQL Server DBAs and analysts will use this tool to determine if additional keys or
indexes may be required. Creation of the additional objects will be determined and
executed by database administrators and analysts.
General Import/Export File Rules
All files imported to and exported from SQL Server 2008 must be delimited. Although commas,
hyphens, semicolons and other characters can be used to delimit files, only a vertical bar (|
or pipe) will be allowed, no exceptions. Using other characters introduces the possibility of
that character being embedded as valid data within a text or large variable character column. A
vertical bar is less likely to be valid data.
Commenting Guidelines
These rules apply in general to all source files.
Object Header Usage
Include object headers in every source code file.
Object Maintenance Documentation
Include object maintenance documentation in every source code file. Keep this
documentation current on all revisions after moving any given source file into production.
Before initial migration into production, revision information is not required.
Comment Quality

MS SQL Server 2008 Naming and Coding Standard.doc

Page 2 of 37

Comments must contain a description of the process the SQL module (source file),
procedure, or trigger accomplishes. Write the description in clear, concise, non-colloquial
English, using business terms to describe the processes.

Do not use comments such as:


--Declare the Customer Name Variable
-DECLARE @CustomerName AS VARCHAR(32)
Describe the purpose of the process and how it works, in non-technical terms. This is
important because most developers can read the SQL and see line-by-line what it does
mechanically, but may not be able to easily grasp the purpose (business logic) behind the
code. Write comments to aid the reader of the code. Assume that the reader is not well
versed in Transact-SQL and try always to describe the how and why of the process
clearly.
Comment Quantity
More comments are better than using fewer comments. Remember that the code will be reviewed
first, then, over time (perhaps after many years), require revisions and enhancements. Providing
adequate comments will allow the database administrator, developer, and the developers peers to
quickly understand the logic and perform necessary revisions, and so forth.
Standard Object Header

Every object contains an object header. An object header contains the database name, object
name, creation date, author, a brief description of the object, parameters passed, returns,
calling mechanism, tables and aliases used in code, other procedures called, DPSR #, notes,
and any special comments and warnings. Module headers remain open on the right-hand side.
Documentation for Object Maintenance
This section describes how to document changes in the Transact-SQL source code. The
method involves using a maintenance documentation heading and comments within the
source code. A common developer signature provides these together.
Object Maintenance Documentation
Immediately following the object header there is a skeletal maintenance block. Use this
skeletal block as a template for maintenance done to the object code. Copy, paste, and fill
in this block when you alter the object code. The block copy marks are on the lines with the
equal signs. Include the lines with the block copy marks.
Make the insertion of the copied block directly after the skeleton block. Thus, the history is
recorded from earliest to latest.
Developer Modification Signature
MS SQL Server 2008 Naming and Coding Standard.doc

Page 3 of 37

Include the developer modification signature in the comment block or function header within
the module that you (the developer) have altered. This gives future developers an easy trail
to follow on what changes have been made and by whom. The signature must contain the
developers three initials followed by the version id, and the number corresponding to the
change of request number, all encompassed within a pair of braces. An example developer
signature follows:
{RHP: 07.01:01}.
------ --------- --|
|
|
|
|
Change ID
|
Version/Release
Developer Initials
Example of OBJECT HEADER AND MAINTENANCE DOCUMENTATION
/*****************************************************************
*
Database:
LIHEAP2000
*
Procedure Name: USP_VENDOR_FILE_DATA
*
Date:
10/02/2002
*
Author:
Steve Jones
*
Procedure Desc:
This proc. returns data required for electronic vendor
*
payment files, currently being sent to vendors in
*
tape format to the top 10 vendors.
*
Parameters:
@idn_vendorAS VARCHAR(10) Vendor ID
*
@nbr_vt_recent AS VARCHAR(15)
Recent VT#
*
@cde_hdr_ftr
AS VARCHAR(10)
*
HEADER/DETAIL/FOOTER
*
Returns:
A recordset with detailed listing of clients with
*
each vendor
*
Calling Mechanism: DTS Package
*
Tables and Alias
T_VOU_TRNSMTL TVT
*
Definitions:
T_VENDOR TV
*
T_APPLN_PMT TAP
*
T_APPLN TA
*
T_VENDOR_EFT_INFO
*
T_STATUS_EFT_MODE
*
Procedures Used: USP_SAVE_ERROR
*
DPSR #:
017480
*
Notes:
*
1. Copyright (c) 2001 Pa Department of Welfare
*
Special Comments/Warnings:
*
None
*
******************************************************************
*
Version:
01.01

MS SQL Server 2008 Naming and Coding Standard.doc

Page 4 of 37

*
Author:
Sumit Dua
*
Date:
12/24/2002
*
DPSR #:
******************************************************************
*
Description of Requests:
*
1. SSN is missing leading 0s
*
Description of Modifications:
*
1. See {SDU:01.01:01} for Coding Changes
*
Special Comments:
*
None
*
Other modules changed with this request:
*
*****************************************************************/
Source Modification Tags
As the developer, after creating a object Maintenance Documentation block, use the
developer modification signature(s) to identify changes to the source code that were made
as per that object maintenance documentation block. Additionally, include a note describing
the change. Together, the signature and note constitute a source modification tag. Use
these tags to record minor level comments. The example segment of source that follows
shows the use of the developer modification signature shown in the above section object
maintenance documentation to record the changes that the developer has applied.

:
:
:
-- {SDU:01.01:01} Make SSN 15 characters
app.NBR_SSN AS VARCHAR(15)
-- {SDU:01.01:01} Put leading zeros back on SSN
RIGHT('000000000' + LTRIM(RTRIM(CAST(
app.NBR_SSN AS VARCHAR(9)))), 9) AS VARCHAR(15)
:
:
:
Major Line-Level Commenting
Line-level code commenting is any other type of commenting that does not fall under object
headers. Major line-level commenting helps clarify where the logic of a certain procedure starts
and ends. Typical instances where major line-level commenting can be used are with
initialization routines, blocks of code which server a logical grouping, validation routines, errorhandling routines, exit routines, and so forth.

MS SQL Server 2008 Naming and Coding Standard.doc

Page 5 of 37

Create major line-level comments in semi-boxes (open on the right) with a multi-line
commenting style. Write a very brief description of the process about which you are
commenting. For detailed explanations, use the major line-level comment block with the minor
line-level comment block (described in the subsequent section).
The routine described in major line-level commenting continues until encountering another
major line-level comment. An example of major line-level commenting follows:
/**********************************************************************
* Initializations
**********************************************************************/
DECLARE @MyStatus AS Integer
SELECT @MyStatus = 0
:
:
:
/**********************************************************************
* Perform Validations
**********************************************************************/
An example of commenting where a logical break is identified and described follows:
/**********************************************************************
* Process Status Code
**********************************************************************/
--- The following code processes the StatusCode parameter by
-- extracting the StatusResolution from the tblSatatus table and
-- verifying that the status is valid with the action parameter.
-IF @StatusCode <> ""
Minor Line-level Commenting
Do not put comments on the same line as the code. Examples of this illegal style follow:
DECLARE @IsNew AS Integer
- Is New Flag
DECLARE @Firstname AS VARCHAR(32) -- First Name Field
Insert all comments for a particular section of code before the code block, with one
blank comment line before and after the comment. An example follows:
--- Declare the local working variables
-DECLARE @IsNew AS Integer
MS SQL Server 2008 Naming and Coding Standard.doc

Page 6 of 37

DECLARE @Firstname AS VARCHAR(32)


--- Assign Initial Values
-SELECT @IsNew = 0,
@FirstName = ""
Formatting Guidelines
Font Style, Size, and Capitalization
Font Style and Size
Write all SQL code in 10 point Courier New font.
Capitalization
Please make sure that Transact-SQL keywords are opposite case from variables, field
names, and object names when coding SQL statements,.
Tab Stops
Set tabs to either four spaces or eight spaces (default).
Line Length
Do not write a Transact-SQL line that exceeds 132 characters in length, unless there is no
other way to properly format the source.
Line Count
Do not write procedures that exceed four pages in length excluding comments, headers,
and maintenance log, without written justification to the data base administrators.
Exceptions to rule are procedures used in conversion or loads of data. Code exceeding
this limit should be looked at closer and possibly split into smaller stored procedures or
modularized.
Star * Usage
Do not use the star symbol * in a Transact-SQL statement, as this is very inefficient. In all
instances, include the appropriate fields in the statements where needed. An example of
the wrong way to write a Transact-SQL statement follows:
SELECT *
FROM T_VENDOR

MS SQL Server 2008 Naming and Coding Standard.doc

Page 7 of 37

Examples of the right way to write a Transact-SQL statement follows. Specify each field as
in the examples:

SELECT
@dte_vt_date = dte_record_crtd
FROM T_VOU_TRNSMTL
WHERE nbr_vt = @nbr_vt_recent
SELECT
pmt.IDN_APPLN_PMT,
pmt.AMT_BNFT
FROM

T_APPLN_PMT pmt

INNER JOIN
ON

T_VOU_TRNSMTL vt

vt.IDN_TRNSMTL_VOU =pmt.IDN_TRNSMTL_VOU

Compound Statements
Compound statements are those that are encased within a BEGINEND structure.
Formatting rules for this structure are as follows:
1. The BEGIN statement aligns directly under the statement above.
2. The END statement is aligned in the same position as its corresponding BEGIN
statement.
3. Within the BEGINEND structure, statements are indented one full tab stop.
IFELSE Statement Blocking
Treat all IF statements (and block ifs) as if they were compound statements using the
BEGINEND structure. No exceptions are permitted.
An example follows:
IF @@ROWCOUNT < 1
BEGIN
RETURN -1
END
ELSE
BEGIN
SELECT @RowReturned = @@ROWCOUNT
RETURN 0
MS SQL Server 2008 Naming and Coding Standard.doc

Page 8 of 37

END

Multi-line Parameter Style


When you must expand a statement to more than one line (because it exceeds the 80character line limit standard), use the style in the following example, for consistent
readability. It is best to apply this style even when you do not exceed the 80-character limit
(though this rule will not be strictly enforced).
EXECUTE

usp_MyStoredProcedure
@MyParameter1,
@MyParameter2,
@MyParameter3 OUTPUT,
@MyParameter4 OUTPUT

Indenting and Formatting SQL Statements


The styling presented in this standard provides for a more consistent look to all stored
procedures and triggers. The guidelines presented here (by example) apply to all TransactSQL statements.
Example of Select Statement
SELECT DISTINCT
t1.nbr_rcpt,
t2.nam_rcpt
FROM t_table1 t1,
t_table2 t2
WHERE t1.nbr_value = t2.nbr_value
AND t1.cde_value = @ParmKeyVal
AND (
t1.nbr_value < 1
OR
t1.nbr_value > 99
)
ORDER BY
t2.nam_rcpt DESC
From the example, it can be seen that:
1. Individual elements are placed in separate lines
2. Elements are indented one tab stop
3. Keywords are to the left whenever possible

MS SQL Server 2008 Naming and Coding Standard.doc

Page 9 of 37

4. If the length of the keyword or keywords (ex: SELECT DISTINCT and ORDER
BY) equals or exceeds the tab stop setting (8), the parameter is placed on the
subsequent line at the proper indent position
5. Parentheses are used to distinguish logical blocks and are indented at the same level as
any other parameter.

Other examples follow:


Example of Select Statement 2
SELECT
DISTINCT t1.nbr_rcpt,
t2.nam_rcpt
FROM t_table1 t1,t_table2 t2
WHERE t1.cde_value = t2.cde_value
AND t1.nbr_rcpt = @ParmKeyVal
AND (t1.cde_value < 1 OR t1.cde_value > 99)
ORDER BY t2.nam_rcpt DESC
Examples of Insert Statements
INSERT
INTO t_vendor
(
nam_vendor,
cde_active,
dte_crtd,
idn_user_crtd
)
VALUES
(
@Name,
@Active,
GETDATE(),
@ModifiedUser
)
INSERT INTO t_vendor(nam_vendor,cde_active,dte_crtd, idn_user_crtd)
VALUES (@Name,@Active,GETDATE(),@ModifiedUser)
Example of Update Statement
UPDATE t_vendor
SET nam_vendor = @Name,
cde_active = @Active,
dte_crtd = GETDATE(),

MS SQL Server 2008 Naming and Coding Standard.doc

Page 10 of 37

idn_user_crtd = @ModifiedUser
WHERE nbr_vendor = @VendorID

Example of Delete Statement


DELETE
FROM t_vendor
WHERE nbr_vendor = @VendorID
Example of Case Statement
CASE @myfield
WHEN 'A' THEN 'ABC '
WHEN 'B' THEN 'BCD '
WHEN 'C' THEN 'CDE '
ELSE 'JKL'
END
Naming Conventions
For table and column names please reference the Information Systems Enterprise Data
Dictionary Standard Abbreviations section on the BIS Web site.
Table Naming
All table names will follow the current DPW naming conventions and should be prefaced
with a T_.
Example:

T_FIPS_CODE

Column Naming
All column names will follow the current DPW naming conventions.
View Naming
All view names will consist of the prefix VW_ followed by business function they perform
or business rule they enforce.
Example: VW_SEL_T_FIPS_CODE

MS SQL Server 2008 Naming and Coding Standard.doc

Page 11 of 37

Variable Formats
All variable names should be defined in a consistent manner across all the programs.
To ensure consistency with the database, variables include the appropriate class word
in the name.
Local Variables: <Class Word>_<Variable Name>
e.g. NBR_INDIV
Parameters: <P>_<Class Word>_<Variable Name>
e.g. P_CNT_INDIV
Global Variables: <G>>_<Class Word>_<Variable Name>
e.g. G_NBR_SSN
Cursors: <Cursor Name>_cur
e.g. ACTIVE_INDIV_CUR

Stored Procedures
When coding enterprise applications, the use of stored procedures to separate application
logic from database code has been found beneficial. Stored procedures will be used for any
data access (SELECT) or manipulation (INSERT, DELETE, UPDATE).
The advantages include:

Ease of Maintenance When supporting database structure or application logic


changes, the DML is easily accessible directly from the database, can be easily
scanned using SQL, and will eliminate the need to interrogate each COM object
when searching for a particular database reference. As such, it is recommended
that all web-based applications use stored procedures for the majority, if not all,
database access.
Enhanced Performance Stored procedures run directly on the database server,
which is optimally tuned to perform such operations.
Reusability Stored procedures can be called from multiple applications, thereby
reducing the time required to design, code and test commonly used application
functions.

Stored procedures should be named according to the business function they perform or
business rule they enforce. The name should include a prefix of USP and a business
description of the action performed. The name should be appropriately abbreviated with items
found in the standard abbreviation list. An example of a procedure name would be
USP_ADD_NEW_T_INDIV. As the name implies, this procedure would add a new row to the
T_INDIV table.

MS SQL Server 2008 Naming and Coding Standard.doc

Page 12 of 37

Triggers
Triggers and sequences should be used to populate artificial keys within a table. Triggers
should also be used to populate the auditing timestamp column on the table.
The naming standard of triggers in the database is defined with concatenation of a trigger
identity string of TR, a one-character trigger event code, a two-character trigger operation
code, a two-character trigger type code, and a descriptive string of table specific information
related to the trigger. The concatenation is undertaken by an underscore between these five
portions of the name. If a particular database does not support one of these descriptors (i.e.
Trigger Event Code), it may be omitted. Definition of the three codes is through following
conventions:
1. Trigger Event Code:
B for BEFORE trigger event, and
A for AFTER trigger event.
2. Trigger Operation Code:
IN for INSERT operation,
UP for UPDATE operation, and
DE for DELETE operation
NS for INSTEAD operation (redirect statement to effect other objects INSTEAD of the
object listed).
LO for Database level operations that occur upon logon
LF for Database level operations that occur upon logoff
ST for Database level operations that occur upon database startup
SD for Database level operations that occur upon database shutdown
ER for operations that occur upon detection of an error condition
SC for schema level operations (typically DDL execution)
3. Trigger Type Code:
RW for ROW LEVEL trigger type, (triggers that are executed for every row affected
during the transaction)
ST for STATEMENT LEVEL trigger types (triggers that execute only one for a
particular transaction no matter how many rows are affected).
For example, if you have a trigger with BEFORE event, for INSERT operation, of ROW LEVEL
type, and acting on a table of APPLN, the trigger will be named as TR_B_IN_RW_APPLN.
Database Rules
Rules are a backward-compatibility feature that perform some of the same functions as
CHECK constraints. Using CHECK constraints is the preferred, standard way to restrict the
values in a column. CHECK constraints are also more concise than rules. There can be only
one rule applied to a column, but multiple CHECK constraints can be applied. CHECK

MS SQL Server 2008 Naming and Coding Standard.doc

Page 13 of 37

constraints are specified as part of the CREATE TABLE statement, while rules are created as
separate objects and then bound to the column.
Constraints
Planning and creating tables requires identifying how to enforce integrity of the data stored
within the columns of the tables. MS SQL Server 2008 has the following constraints to enforce
integrity:
Primary Key Constraints are columns or a column that uniquely identifies a row within
a table. All tables should have a primary key and composite primary keys should be
avoided. Primary keys should be named PK_tablename_columnname.
Example: PK_T_ADDRESS_IDN_ADDR.
Foreign Key Constraints are columns or a column that is used to enforce a relation
between information in two tables. A link is defined between the two tables when a
primary key is referenced by a column or columns in another table. The reference
becomes a foreign key in the second table. Foreign keys should be named
FK_foreignkeytable_primarykeytable_columnname.
Example: FK_T_NAME_T_ADDRESS_IDN_ADDR.
Unique Constraints are columns or a column defined on a table to ensure that no
duplicate values can exist. Unique constraints can be used to enforce uniqueness of
data on columns that are not a primary key. More than one unique constrain can be
defined on a table. Unique constraints should be named UCON_
tablename_columnname.
Example: UCON_T_ADDR_IDN_ADDR.
Check Constraints are created to limit values that can be entered in a column or
columns. Unlike foreign key constraints, a check constraint determines valid data from a
logical expression. Check constraints can be used to limit valid data entries to a range.
Check constraints should be named CKCON_ tablename_columnname.
Example: CKCON_T_ADDR_STATE.
Indexes
An index is an on-disk structure associated with a table or a view that speeds retrieval of rows
from the table or the view. An index contains keys built from one or more columns in the table
or the view. These keys are stored in a structure (B-tree) that enables SQL Server to find the
row or rows associated with the key values quickly and efficiently.
A table or view can contain the following types of indexes:

MS SQL Server 2008 Naming and Coding Standard.doc

Page 14 of 37

Clustered
o

Clustered indexes sort and store the data rows in the table or view based on their
key values. These are the columns included in the index definition. There can be
only one clustered index per table, because the data rows themselves can be
sorted in only one order.

The only time the data rows in a table are stored in sorted order is when the table
contains a clustered index. When a table has a clustered index, the table is
called a clustered table. If a table has no clustered index, its data rows are stored
in an unordered structure called a heap.

Nonclustered
o

Nonclustered indexes have a structure separate from the data rows. A


nonclustered index contains the nonclustered index key values and each key
value entry has a pointer to the data row that contains the key value.

The pointer from an index row in a nonclustered index to a data row is called a
row locator. The structure of the row locator depends on whether the data pages
are stored in a heap or a clustered table. For a heap, a row locator is a pointer to
the row. For a clustered table, the row locator is the clustered index key.

Indexes should be named IDX_ tablename_columnname(s).


Example:

IDX_T_ADDR_STATE_STATE

Default Definitions
Default Definitions are predefined values created on columns within a table. The default
value will be used when inserting a new row in a table and a particular column in the
insert statement does not have a value. For example you may have a default of all
99999999 that you may want to set up as a default end date. The default should have a
prefix of D_ followed by a description of what the default does. All words should be run
together and not be separated by underscores.
Example: D_OPENDATE
Allowing Null Values is used when a column in a row can contain a null value. NULL is a
value either unknown or undefined and typically means no entry has been made. Avoid
permitting null values within columns.
User Defined Data Types
User defined Data Types are predefined variable lengths. For example instead of having to
always specify that SSN is a numeric field of 10 positions, you could create a user defined data
type entitled udt_ssn. Therefore, when you need to use SSN, you would just specify udt_ssn

MS SQL Server 2008 Naming and Coding Standard.doc

Page 15 of 37

as the data type and all the relevant information would not have to be reentered. The user
defined data type should have a prefix of UDT_ followed by a description of what the data type
is supposed to standardize. All words should run together and not be separated by
underscores.
Example: UDT_CITY

User Defined Functions


User defined functions can be created in SQL Server 2008 to perform actions such as
complicated calculations or character string manipulation. User defined functions should be
created and used for actions repeated within an application. An example would be for adding
dashes to social security number. This user defined function could be created and used any
time a correctly formatted social security number is required. User defined functions should be
named UDF_function.
Example: UDF_FORMATSSN

Transaction and Error Handling


TryCatch (copied from SQL Server 2008 Books On Line)
The new construct, TryCatch, was introduced in SQL Server 2005. This method will be used
in place of the old RAISEERROR and transactional t-sql as documented in previous versions
of SQL Server Development Standards.
Errors in Transact-SQL code can be processed by using a TRYCATCH construct similar to
the exception-handling features of the Microsoft Visual C++ and Microsoft Visual C#
languages. A TRYCATCH construct consists of two parts: a TRY block and a CATCH block.
When an error condition is detected in a Transact-SQL statement that is inside a TRY block,
control is passed to a CATCH block where the error can be processed.
After the CATCH block handles the exception, control is then transferred to the first TransactSQL statement that follows the END CATCH statement. If the END CATCH statement is the
last statement in a stored procedure or trigger, control is returned to the code that invoked the
stored procedure or trigger. Transact-SQL statements in the TRY block following the statement
that generates an error will not be executed.
If there are no errors inside the TRY block, control passes to the statement immediately after
the associated END CATCH statement. If the END CATCH statement is the last statement in a
stored procedure or trigger, control is passed to the statement that invoked the stored
procedure or trigger.

MS SQL Server 2008 Naming and Coding Standard.doc

Page 16 of 37

A TRY block starts with the BEGIN TRY statement and ends with the END TRY statement.
One or more Transact-SQL statements can be specified between the BEGIN TRY and END
TRY statements.
A TRY block must be followed immediately by a CATCH block. A CATCH block starts with the
BEGIN CATCH statement and ends with the END CATCH statement. In Transact-SQL, each
TRY block is associated with only one CATCH block.
It is highly recommended that the return variables from any error trapping be returned to the
application and processed at the application. Avoid processing an exception or error that
causes the application to abnormally end.
Working with TRYCATCH
When you use the TRYCATCH construct, consider the following guidelines and suggestions:

Each TRYCATCH construct must be inside a single batch, stored procedure, or


trigger. For example, you cannot place a TRY block in one batch and the associated
CATCH block in another batch. The following script would generate an error:
BEGIN TRY
SELECT *
FROM sys.messages
WHERE message_id = 21;
END TRY
GO
-- The previous GO breaks the script into two batches,
-- generating syntax errors. The script runs if this GO
-- is removed.
BEGIN CATCH
SELECT ERROR_NUMBER() AS ErrorNumber;
END CATCH;
GO

A TRY block must be immediately followed by a CATCH block.

TRYCATCH constructs can be nested. This means that TRYCATCH constructs can
be placed inside other TRY and CATCH blocks. When an error occurs within a nested
TRY block, program control is transferred to the CATCH block that is associated with
the nested TRY block.

To handle an error that occurs within a given CATCH block, write a TRY...CATCH
block within the specified CATCH block.

MS SQL Server 2008 Naming and Coding Standard.doc

Page 17 of 37

Errors that have a severity of 20 or higher that cause the Database Engine to close the
connection will not be handled by the TRYCATCH block. However, TRYCATCH will
handle errors with a severity of 20 or higher as long as the connection is not closed.

Errors that have a severity of 10 or lower are considered warnings or informational


messages, and are not handled by TRYCATCH blocks.

Attentions will terminate a batch even if the batch is within the scope of a TRYCATCH
construct. This includes an attention sent by the Microsoft Distributed Transaction
Coordinator (MS DTC) when a distributed transaction fails. MS DTC manages
distributed transactions.

Error Functions
TRYCATCH uses the following error functions to capture error information:

ERROR_NUMBER() returns the error number.

ERROR_MESSAGE() returns the complete text of the error message. The text includes
the values supplied for any substitutable parameters such as lengths, object names, or
times.

ERROR_SEVERITY() returns the error severity.

ERROR_STATE() returns the error state number.

ERROR_LINE() returns the line number inside the routine that caused the error.

ERROR_PROCEDURE() returns the name of the stored procedure or trigger where the
error occurred.

Error information is retrieved by using these functions from anywhere in the scope of the
CATCH block of a TRYCATCH construct. The error functions will return NULL if called
outside the scope of a CATCH block. Error functions can be referenced inside a stored
procedure and can be used to retrieve error information when the stored procedure is
executed in the CATCH block. By doing this, you do not have to repeat the error handling code
in every CATCH block. In the follow code example, the SELECT statement in the TRY block
will generate a divide-by-zero error. The error will be handled by the CATCH block, which uses
a stored procedure to return error information.

USE AdventureWorks;
GO
-- Verify that the stored procedure does not exist.
IF OBJECT_ID ('usp_GetErrorInfo', 'P') IS NOT NULL
DROP PROCEDURE usp_GetErrorInfo;

MS SQL Server 2008 Naming and Coding Standard.doc

Page 18 of 37

GO
-- Create a procedure to retrieve error information.
CREATE PROCEDURE usp_GetErrorInfo
AS
SELECT
ERROR_NUMBER() AS ErrorNumber,
ERROR_SEVERITY() AS ErrorSeverity,
ERROR_STATE() as ErrorState,
ERROR_PROCEDURE() as ErrorProcedure,
ERROR_LINE() as ErrorLine,
ERROR_MESSAGE() as ErrorMessage;
GO
BEGIN TRY
-- Generate divide-by-zero error.
SELECT 1/0;
END TRY
BEGIN CATCH
-- Execute the error retrieval routine.
EXECUTE usp_GetErrorInfo;
END CATCH;
GO
Compile and Statement-level Recompile Errors
There are two types of errors that will not be handled by TRYCATCH if the error occurs in
the same execution level as the TRYCATCH construct:

Compile errors, such as syntax errors that prevent a batch from executing.

Errors that occur during statement-level recompilation, such as object name resolution
errors that happen after compilation due to deferred name resolution.

When the batch, stored procedure, or trigger that contains the TRYCATCH construct
generates one of these errors, the TRYCATCH construct does not handle these errors.
These errors will return to the application or batch that called the error-generating routine. For
example, the following code example shows a SELECT statement that causes a syntax error.
If this code is executed in the SQL Server Management Studio Query Editor, execution will not
start because the batch fails to compile. The error will be returned to the Query Editor and will
not get caught by TRYCATCH.

USE AdventureWorks;
GO
BEGIN TRY
MS SQL Server 2008 Naming and Coding Standard.doc

Page 19 of 37

-- This PRINT statement will not run because the batch


-- does not begin execution.
PRINT N'Starting execution';
-- This SELECT statement contains a syntax error that
-- stops the batch from compiling successfully.
SELECT ** FROM HumanResources.Employee;
END TRY
BEGIN CATCH
SELECT
ERROR_NUMBER() AS ErrorNumber,
ERROR_MESSAGE() AS ErrorMessage;
END CATCH;
GO
Unlike the syntax error in the previous example, an error that occurs during statement-level
recompilation will not prevent the batch from compiling, but it will terminate the batch as soon
as recompilation for the statement fails. For example, if a batch has two statements and the
second statement references a table that does not exist, deferred name resolution causes the
batch to compile successfully and start execution without binding the missing table to the query
plan until that statement is recompiled. The batch stops running when it gets to the statement
that references the missing table and returns an error. This type of error will not be handled by
a TRYCATCH construct at the same level of execution at which the error occurred. The
following example demonstrates this behavior.

USE AdventureWorks;
GO
BEGIN TRY
-- This PRINT statement will run because the error
-- occurs at the SELECT statement.
PRINT N'Starting execution';
-- This SELECT statement will generate an object name
-- resolution error because the table does not exist.
SELECT * FROM NonExistentTable;
END TRY
BEGIN CATCH
SELECT
ERROR_NUMBER() AS ErrorNumber,
ERROR_MESSAGE() AS ErrorMessage;
END CATCH;
GO

MS SQL Server 2008 Naming and Coding Standard.doc

Page 20 of 37

You can use TRYCATCH to handle errors that occur during compilation or statement-level
recompilation by executing the error-generating code in a separate batch within the TRY block.
For example, you do this by placing the code in a stored procedure or by executing a dynamic
Transact-SQL statement using sp_executesql. This allows TRYCATCH to catch the error at
a higher level of execution than the error occurrence. For example, the following code shows a
stored procedure that generates an object name resolution error. The batch that contains the
TRYCATCH construct is executing at a higher level than the stored procedure; and the error,
which occurs at a lower level, is caught.

USE AdventureWorks;
GO
-- Verify that the stored procedure does not already exist.
IF OBJECT_ID ('usp_MyError', 'P') IS NOT NULL
DROP PROCEDURE usp_MyError;
GO
CREATE PROCEDURE usp_MyError
AS
-- This SELECT statement will generate
-- an object name resolution error.
SELECT * FROM NonExistentTable;
GO
BEGIN TRY
-- Run the stored procedure.
EXECUTE usp_MyError;
END TRY
BEGIN CATCH
SELECT
ERROR_NUMBER() AS ErrorNumber,
ERROR_MESSAGE() AS ErrorMessage;
END CATCH;
GO
Here is the result set.

ErrorNumber ErrorMessage
----------- --------------------------------------208
Invalid object name 'NonExistentTable'.

MS SQL Server 2008 Naming and Coding Standard.doc

Page 21 of 37

Handling Deadlocks
TRYCATCH can be used to handle deadlocks. The 1205 deadlock victim error can be
caught by the CATCH block and the transaction can be rolled back until the threads become
unlocked
The following example shows how TRYCATCH can be used to handle deadlocks. This first
section creates a table that will be used to demonstrate a deadlock state and a stored
procedure that will be used to print error information.

USE AdventureWorks;
GO
-- Verify that the table does not exist.
IF OBJECT_ID (N'my_sales',N'U') IS NOT NULL
DROP TABLE my_sales;
GO
-- Create and populate the table for deadlock simulation.
CREATE TABLE my_sales
(
Itemid
INT PRIMARY KEY,
Sales
INT not null
);
GO
INSERT my_sales (itemid, sales) VALUES (1, 1);
INSERT my_sales (itemid, sales) VALUES (2, 1);
GO
-- Verify that the stored procedure for error printing
-- does not exist.
IF OBJECT_ID (N'usp_MyErrorLog',N'P') IS NOT NULL
DROP PROCEDURE usp_MyErrorLog;
GO
-- Create a stored procedure for printing error information.
CREATE PROCEDURE usp_MyErrorLog
AS
PRINT
'Error ' + CONVERT(VARCHAR(50), ERROR_NUMBER()) +
', Severity ' + CONVERT(VARCHAR(5), ERROR_SEVERITY()) +
', State ' + CONVERT(VARCHAR(5), ERROR_STATE()) +
', Line ' + CONVERT(VARCHAR(5), ERROR_LINE());
PRINT

MS SQL Server 2008 Naming and Coding Standard.doc

Page 22 of 37

ERROR_MESSAGE();
GO
The following code scripts for session 1 and session 2 run simultaneously in two separate SQL
Server Management Studio connections. Both sessions try to update the same rows in the
table. One of the sessions will succeed with the update operation during the first attempt, and
the other session will be selected as the deadlock victim. The deadlock victim error will cause
execution to jump to the CATCH block and the transaction will enter an uncommittable state.
Inside the CATCH block, the deadlock victim can roll back the transaction and retry updating
the table until the update succeeds or the retry limit is reached, whichever happens first.

Session 1

Session 2

USE AdventureWorks;
GO

USE AdventureWorks;
GO

-- Declare and set variable


-- to track number of retries
-- to try before exiting.
DECLARE @retry INT;
SET @retry = 5;

-- Declare and set variable


-- to track number of retries
-- to try before exiting.
DECLARE @retry INT;
SET @retry = 5;

-- Keep trying to update


-- table if this task is
-- selected as the deadlock
-- victim.
WHILE (@retry > 0)
BEGIN
BEGIN TRY
BEGIN TRANSACTION;

--Keep trying to update


-- table if this task is
-- selected as the deadlock
-- victim.
WHILE (@retry > 0)
BEGIN
BEGIN TRY
BEGIN TRANSACTION;

UPDATE my_sales
SET sales = sales + 1
WHERE itemid = 1;

UPDATE my_sales
SET sales = sales + 1
WHERE itemid = 2;

WAITFOR DELAY '00:00:13';

WAITFOR DELAY '00:00:07';

UPDATE my_sales
SET sales = sales + 1
WHERE itemid = 2;

UPDATE my_sales
SET sales = sales + 1
WHERE itemid = 1;

SET @retry = 0;

SET @retry = 0;

MS SQL Server 2008 Naming and Coding Standard.doc

Page 23 of 37

COMMIT TRANSACTION;
END TRY
BEGIN CATCH
-- Check error number.
-- If deadlock victim error,
-- then reduce retry count
-- for next update retry.
-- If some other error
-- occurred, then exit
-- retry WHILE loop.
IF (ERROR_NUMBER() = 1205)
SET @retry = @retry - 1;
ELSE
SET @retry = -1;

COMMIT TRANSACTION;
END TRY
BEGIN CATCH
-- Check error number.
-- If deadlock victim error,
-- then reduce retry count
-- for next update retry.
-- If some other error
-- occurred, then exit
-- retry WHILE loop.
IF (ERROR_NUMBER() = 1205)
SET @retry = @retry - 1;
ELSE
SET @retry = -1;

-- Print error information.


EXECUTE usp_MyErrorLog;
IF XACT_STATE() <> 0
ROLLBACK TRANSACTION;
END CATCH;
END; -- End WHILE loop.
GO

-- Print error information.


EXECUTE usp_MyErrorLog;
IF XACT_STATE() <> 0
ROLLBACK TRANSACTION;
END CATCH;
END; -- End WHILE loop.
GO

TRYCATCH with RAISERROR


RAISERROR can be used in either the TRY or CATCH block of a TRYCATCH construct to
affect error-handling behavior.
RAISERROR that has a severity of 11 to 19 executed inside a TRY block causes control to
transfer to the associated CATCH block. RAISERROR that has a severity of 11 to 19 executed
inside a CATCH block returns an error to the calling application or batch. In this way,
RAISERROR can be used to return information to the caller about the error that caused the
CATCH block to execute. Error information provided by the TRYCATCH error functions can
be captured in the RAISERROR message, including the original error number; however, the
error number for RAISERROR must be >= 50000.
RAISERROR that has a severity 10 or lower returns an informational message to the calling
batch or application without invoking a CATCH block.
RAISERROR that has a severity 20 or higher closes the database connection without invoking
the CATCH block.
The following code example shows how RAISERROR can be used inside a CATCH block to
return the original error information to the calling application or batch. The stored procedure

MS SQL Server 2008 Naming and Coding Standard.doc

Page 24 of 37

usp_GenerateError executes a DELETE statement inside a TRY block that generates a


constraint violation error. The error causes execution to transfer to the associated CATCH
block inside usp_GenerateError where the stored procedure usp_RethrowError is executed to
raise the constraint violation error information using RAISERROR. This error generated by
RAISERROR is returned to the calling batch where usp_GenerateError was executed and
causes execution to transfer to the associated CATCH block in the calling batch.

Note:
RAISERROR can generate errors with state from 1 through 127 only. Because the Database
Engine might raise errors with state 0, we recommend that you check the error state returned
by ERROR_STATE before passing it as a value to the state parameter of RAISERROR.
USE AdventureWorks;
GO
-- Verify that stored procedure does not exist.
IF OBJECT_ID (N'usp_RethrowError',N'P') IS NOT NULL
DROP PROCEDURE usp_RethrowError;
GO
-- Create the stored procedure to generate an error using
-- RAISERROR. The original error information is used to
-- construct the msg_str for RAISERROR.
CREATE PROCEDURE usp_RethrowError AS
-- Return if there is no error information to retrieve.
IF ERROR_NUMBER() IS NULL
RETURN;
DECLARE
@ErrorMessage NVARCHAR(4000),
@ErrorNumber INT,
@ErrorSeverity INT,
@ErrorState
INT,
@ErrorLine
INT,
@ErrorProcedure NVARCHAR(200);
-- Assign variables to error-handling functions that
-- capture information for RAISERROR.
SELECT
@ErrorNumber = ERROR_NUMBER(),
@ErrorSeverity = ERROR_SEVERITY(),
@ErrorState = ERROR_STATE(),
@ErrorLine = ERROR_LINE(),
@ErrorProcedure = ISNULL(ERROR_PROCEDURE(), '-');
MS SQL Server 2008 Naming and Coding Standard.doc

Page 25 of 37

-- Build the message string that will contain original


-- error information.
SELECT @ErrorMessage =
N'Error %d, Level %d, State %d, Procedure %s, Line %d, ' +
'Message: '+ ERROR_MESSAGE();
-- Raise an error: msg_str parameter of RAISERROR will contain
-- the original error information.
RAISERROR
(
@ErrorMessage,
@ErrorSeverity,
1,
@ErrorNumber, -- parameter: original error number.
@ErrorSeverity, -- parameter: original error severity.
@ErrorState, -- parameter: original error state.
@ErrorProcedure, -- parameter: original error procedure name.
@ErrorLine
-- parameter: original error line number.
);
GO
-- Verify that stored procedure does not exist.
IF OBJECT_ID (N'usp_GenerateError',N'P') IS NOT NULL
DROP PROCEDURE usp_GenerateError;
GO
-- Create a stored procedure that generates a constraint violation
-- error. The error is caught by the CATCH block where it is
-- raised again by executing usp_RethrowError.
CREATE PROCEDURE usp_GenerateError
AS
BEGIN TRY
-- A FOREIGN KEY constraint exists on the table. This
-- statement will generate a constraint violation error.
DELETE FROM Production.Product
WHERE ProductID = 980;
END TRY
BEGIN CATCH
-- Call the procedure to raise the original error.
EXEC usp_RethrowError;
END CATCH;
GO
-- In the following batch, an error occurs inside
-- usp_GenerateError that invokes the CATCH block in

MS SQL Server 2008 Naming and Coding Standard.doc

Page 26 of 37

-- usp_GenerateError. RAISERROR inside this CATCH block


-- generates an error that invokes the outer CATCH
-- block in the calling batch.
BEGIN TRY -- outer TRY
-- Call the procedure to generate an error.
EXECUTE usp_GenerateError;
END TRY
BEGIN CATCH -- Outer CATCH
SELECT
ERROR_NUMBER() as ErrorNumber,
ERROR_MESSAGE() as ErrorMessage;
END CATCH;
GO
Changing the Flow of Execution
To change the flow of execution, GOTO can be used within a TRY block or a CATCH block.
GOTO can also be used to exit a TRY block or a CATCH block; however, GOTO cannot be
used to enter a TRY block or a CATCH block.
Nested Error-handling Example
The following example shows using nested TRYCATCH constructs.

BEGIN TRY
BEGIN TRY
SELECT CAST('invalid_date' AS datetime)
END TRY
BEGIN CATCH
PRINT 'Inner TRY error number: ' +
CONVERT(varchar,ERROR_NUMBER()) + ' on line: ' +
CONVERT(varchar, ERROR_LINE())
END CATCH
SELECT CAST('invalid_int' AS int)
END TRY
BEGIN CATCH
PRINT 'Outer TRY error mumber: ' + CONVERT(varchar,ERROR_NUMBER())+
' on line: ' + CONVERT(varchar, ERROR_LINE())
END CATCH
Custom Error Messages
Generation of custom error messages for use by procedures and triggers is optional but highly
recommended.

MS SQL Server 2008 Naming and Coding Standard.doc

Page 27 of 37

Custom Error Numbering


Assign numbers above 50000 to custom error messages to conform to the MS SQL
standard. Database Administrators will provide developers the range of numbers to use, to
ensure that the error numbers of multiple database projects do not overlap. The maximum
value allowed for custom error numbers is 2,147,483,647.
Adding and Removing Custom Error Messages
Add custom error messages to the MS SQL Master DB with the sp_addmessage built-in
stored procedure. Generate a script file containing all error messages and submit it to the
Database Administrators. This script should also be stored in Source Safe or Team
Foundation Server. See the following example for proper formatting:
--Message Table Errors
sp_addmessage 50001, 16, "The specified message was not found."
go

Transact-SQL Restrictions
Following is SQL Server functionality that should not be used when developing procedures or
triggers.
Cursors
Do not use cursors without adequate justification and the consent of the Database
Administrators. Use alternatives to cursors wherever possible, including: temp tables, table
variables, redefinition of logic, and exterior 3GL coding (for example: a Visual Basic
program or process).
When a cursor is declared with a FOR UPDATE clause, it is recommended to specify a
column list. SQL Server can optimize operations based on column list information
available. A future release of SQL Server may have tighter requirements on specification of
updateable columns.
Please make sure to use FOR UPDATE clause when updates are done through the cursor.
When a cursor is declared with a FOR UPDATE clause and no updates occur through the
cursor do not use the FOR UPDATE statement.
Result Sets for Qualified Transactions
Develop procedures so that each procedure will return a maximum of one result set, if the
procedure will be used with qualified transactions.
Result Sets for Non-Qualified Transactions
It is recommended that, when developing procedures, create each procedure so that it
returns only one result set, when that procedure is for use with non-qualified transactions.

MS SQL Server 2008 Naming and Coding Standard.doc

Page 28 of 37

Development of individual stored procedures is recommended, when business logic


dictates that a procedure return multiple result sets.
Use of *= and =* and SQL JOINS
Do not use of the *= and =* join operators in the WHERE clause. Use instead the JOIN
keyword in the FROM clause. Microsoft does not recommend the use of *= and =* and
may not provide support for these in future releases of MS SQL Server. Using this syntax
for joins is discouraged (by Microsoft) because of the potential for ambiguous interpretation
and because it is nonstandard. Be sure to use the join syntax. Instead of using where a = b
and b = c, for example, use inner join on a = b, etc. Use left and right outer joins as well as
UNION ALLS sparingly. Pick the join order carefully. The majority of outer joins can
successfully be rewritten as inner joins with tremendous performance improvements.

Use of SELECT INTO


Do not use the SELECT INTO clause to create a table on the fly. Instead, create your table
before the SELECT statement and use the INSERT statement followed by the appropriate
SELECT.
Usage of Column Prefixes
It is generally recommended to use prefixes for columns appearing in the SELECT list. This
is a coding best practice that leads to more maintainable applications.
As an example, it is preferred to have:
SELECT a.au_id, a.au_lname FROM dbo.authors a
over:
SELECT au_id, au_lname FROM dbo.authors
Database Sql Options
Database SQL Options should be configured as recommended to remove deprecated
behaviors, be ANSI compliant, and be able to leverage the full feature set (indexed views
and indexes on computed columns).
The following SQL options should be checked to see if they are configured properly as per
Microsoft SQL Options control ANSI compliance options.
The following database SQL Options should be ON:

ANSI_NULLS
ANSI_PADDING
ANSI_WARNINGS
ARITHABORT
CONCAT_NULL_YIELDS_NULL
QUOTED_IDENTIFIERS

MS SQL Server 2008 Naming and Coding Standard.doc

Page 29 of 37

The following should be OFF:

NUMERIC_ROUNDABOUT

Use of Defaults and Rules


Database Administrators will check stored procedures, functions, views and triggers for
existence of defaults and rules.
These objects have been deprecated in favor of CHECK constraints and will not be
supported in a future release of SQL Server.
Null Comparisons
Database Administrators will check stored procedures, views, functions and triggers to flag
the use of equality and inequality comparisons involving a NULL constant. These
comparisons are undefined when ANSI_NULLS option is set to ON.
It is recommended to set ANSI_NULLS to ON and use the IS keyword to compare against
NULL constants.
String = Expression Aliasing
Database Administrators will check stored procedures, functions, views and triggers for use
of column aliases where the name of the expression uses a string value. It is recommended
to use quoted identifiers instead. String aliases will not be supported in a future release of
SQL Server.
As an example, the following syntax is not recommended:
SELECT 'alias_for_col'=au_id+au_id FROM dbo.authors
Recommended alternatives are:
SELECT au_id+au_id as "alias_for_col" FROM dbo.authors
SELECT au_id+au_id as alias_for_col FROM dbo.authors
SELECT au_id+au_id as [alias_for_col] FROM dbo.authors
Primary Keys
Database Administrators will look at all user databases to ensure that all tables have
defined either a primary key or have a column with a Unique Constraint defined. Tables
that dont have a Primary Key defined or a column with a unique constraint defined will
suffer from bad performance.
Foreign Keys
Database Administrators will look at all user databases to ensure that proper foreign
key/primary key relationships are created when needed. Foreign keys should also be
defined as indexes.
Order by Clause with Ordinals

MS SQL Server 2008 Naming and Coding Standard.doc

Page 30 of 37

Database Administrators will check stored procedures, functions, views and triggers for
use of ORDER BY clause specifying ordinal column numbers as sort columns.
As an example, the following syntax is not allowed:
SELECT au_id FROM dbo.authors ORDER BY 2, 1
The use of ordinal column numbers will not be allowed.
Schema Usage
Database Administrators will check stored procedures, functions, views and triggers for
use of schema qualified names when referencing tables and views.
Unless specified otherwise, all Transact-SQL references to the name of a database object
can be a four-part name in the form:
[
server_name.[database_name].[schema_name].
| database_name.[schema_name].
| schema_name.
]
]
When referencing a specific object, you do not always have to specify the server,
database, and owner (schema) for SQL Server to identify the object. However, it is
recommended that at least the schema name be specified to identify a table or view inside
a stored procedure, function, view or trigger.
When SQL Server looks up a table/view without a schema qualification, it first looks in the
default schema and then looks in the 'dbo' schema. The default schema corresponds to
the current user for adhoc batches, and corresponds to the schema of a stored procedure
when inside one. In either case, SQL Server incurs an additional runtime cost to verify
schema binding of unqualified objects. Applications are more maintainable and may
experience a slight performance improvement if object references are schema qualified.
Top without Order By
Database Administrators will check stored procedures, functions, views and triggers for
usages of TOP in queries without an ORDER BY clause.
It is generally recommended to specify sort criteria when using TOP clause. Otherwise, the
results produced will be plan dependant and may lead to undesired behavior.
With Hint Specification
Database Administrators will check stored procedures, triggers, views and functions for
use of table hints without the WITH keyword.
Hints must be specified using the WITH keyword.
Avoid Stored Procedure Recompiles
See next section.

MS SQL Server 2008 Naming and Coding Standard.doc

Page 31 of 37

Production Adhoc Querying


There will be no real time adhoc reporting against the production database. ACCESS is not
allowed to connect to SQL to do reporting as this will cause table locks and performance
degradation and create a security risk.
Hard coded SQL
There will be no hard coded SQL in web pages or applications. Put T-SQL in stored
procedures for security reasons, efficiency and ease in debugging.

Performance Recommendations
General Considerations
Connections to the SQL Server database should be brief. A connection should be made,
a process completed and the connection dropped. No persistent connections to SQL
Server are allowed.
Stored procedures should perform one process and not a series of process. It is better to
create a stored procedure that performs an insert rather than a stored procedure that
performs an insert, update and a delete. Executing multiple stored procedures is preferred
over executing one, unwieldy, multi-task stored procedure.
Stored Procedure Recompiles
No stored procedures should interleave Data Definition Language (DDL) and Data
Manipulation Language (DML) operations. In other words, do all CREATE/DROP
statements separate from SELECT and UPDATE statements. If data operations are mixed,
the SQL server must recompile procedures each time to determine the best plan to use for
each new or dropped object. Therefore, coding all creates and drops together will reduce
the amount of stored procedure compiles. Avoid creating a temporary table in a control-offlow statement such as an IFELSE or WHILE statement. Avoid using a DROP statement
for temporary tables in stored procedures, because tables are automatically dropped when
the procedure is completed. Specifying the owner of objects such as dbo.Tablename and
dbo.Stored procedure name will cut down on recompiles.
Temporary Tables
Often procedure throughput can be dramatically improved by the use of temporary tables.
Restructuring a Transact-SQL statement that involves lengthy and/or complicated joins to
perform the same functionally by multiple actions against a temp table will improve
performance. Please note: use a temporary table to manipulate small amounts of data,

MS SQL Server 2008 Naming and Coding Standard.doc

Page 32 of 37

and if data exceeds 500 records add an index. Also if you have > 6 data changes in a
temporary table consider using the option (keep plan) to avoid unnecessary recompiles.
Table Variable Type
The table variable type functions exactly like the temporary table, with the exception that all
data is loaded directly in random access memory.
Temporary Tables vs. Table Variable
Database Administrators will check stored procedures and triggers for usages of temporary
tables that may be replaced by use of table variables.
When a procedure creates a temporary table and has no CREATE INDEX issued on it, and
it is dropped all in the same procedure, you will be asked to consider using table variables
instead to potentially observe fewer recompilations.
Note that if large data volumes will be inserted in the temporary table it may still be
preferred to use temporary tables over table variables due to parallel execution restrictions
and statistics maintenance.
Dynamic SQL Execution
Where possible use the stored procedure sp_executesql to execute dynamic code rather
then the straight exec command. The procedure sp_executesql should be used instead of
the exec command to execute a Transact-SQL statement a number of times when the
change in parameter values to the statement is the only variation. This is because the
Transact-SQL SQL Server query optimizer is likely to reuse the execution plan it generates
for the first execution.
Table Hints
Consider using Table Hints to optimize performance.
Join Hints
Consider using Join Hints to optimize performance.
Indexes
Examine the performance of the statements within the procedure and the entire application
to determine if an additional index will justify its overhead. Consider overall index usage all
along the development path. Indexes, or lack of indexes, are the direct cause of poor
performance 90% of the time. As per Microsofts suggestion, all tables that have more then
500 records should have a clustered index, even if a field is created just to hold the index.
Please note when setting an index, pay close attention to the fill factor for the given index.
The fill factor is the amount space consumed by the index and more importantly the amount
of space that will be kept free for expansion. For an example a 90% fill factor is 90% full
with 10% left for expansion. For tables that do a high volume of inserts, updates, deletes
you may want to consider a lower fill factor, or perhaps not having a clustered index at all.
A clustered index will slow down inserts but speed up selects. The developer should
determine what the best trade off is.

MS SQL Server 2008 Naming and Coding Standard.doc

Page 33 of 37

Ultimately, it is the responsibility of the developer coding the stored procedures and the
SQL code to determine what the best indexes are and how they should be used.
Execution Plan
When debugging and tuning a stored procedure, examine the execution plan for clues as to
the performance of the procedure and how it may be improved.
In the example below, a table scan is being generated. From this, one should ask if another
index should be created to avoid the scan. If yes, create an index and rerun.

All table scans should be thoroughly documented. An example execution plan should be
checked into Source Safe or Team Foundation Server as well as the sql code checklist and
changed as the code changes. If there are various scenarios be sure to submit all of the
various paths, especially the worst case path. See the execution plan example doc below on
how to format the plan for submission to DBA.
SQL Profiler/Database Engine Tuning Advisor
Two valuable and necessary tools when tracking and or monitoring performance are SQL Profiler and
Database Engine Tuning Advisor. Only the DBA has permissions to run these two tools. The DBA can
analyze the output from these tools and give suggestions on what tables or objects have problems and
can benefit from index creation.

Programmatic Recommendations
This section provides recommendations to developers for improving the reliability of
procedures and triggers.
Use SET NOCOUNT
When developing procedures that return a result set and use intermediate processing, be
sure to set the SET NOCOUNT ON option to prevent the generation of unwanted result set
count information. Use the SET NOCOUNT OFF statement to restore result counting
before executing a SELECT statement to return results.
Stylistic Recommendations

MS SQL Server 2008 Naming and Coding Standard.doc

Page 34 of 37

This section provides recommendations to developers for improving the readability of


procedures and triggers.
Nesting IF statements
Avoid, wherever possible, nesting IF statements. Use compound Boolean expressions
instead.

SQL Server 2008 Integration Services


SQL Server 2008 Integration Services (SSIS) is required when performing extract, transform
and load (ETL) processing. SSIS replaces SQL Server 2000 DTS and brings enhancements
and improvements to creating and executing packages.

Naming Packages
The name of the SQL Server Integration Services package should indicate the database the
package belongs to as well as the purpose of the SSIS package. In a development
environment, the developer testing the package may own the package but in all other
environments SA (System Administrator) will own the package. The package may very well
have the same name as the job. The difference between a job and a package is that a job is a
scheduled package. A package on its own must be started manually. The package names can
be mixed case for ease of readability.
Example: CUSTOMERVendorExport
SQL Server 2008 Reporting Services
SQL Server 2008 Reporting Services (SSRS) is required when creating any reports for use at
the DPW.
Naming Reports
The name of the SQL Server 2008 Reporting Services object should indicate the database the
report belongs to as well as the purpose of the SSRS package. The report names can be
mixed case for ease of readability.
Example: CUSTOMERNameAndAddresses
Job Naming Standards
A job consists of a series of SQL Statements that are to be executed as a transaction (as one
complete unit). The name of a SQL SERVER job should indicate the database the job belongs

MS SQL Server 2008 Naming and Coding Standard.doc

Page 35 of 37

to and the purpose of the job as well. In a development environment, the job may be owned by
the developer testing the job; but, in all other environments the SA will own the job. All jobs will
need to be approved by the DBA prior to being scheduled to run. The job names should be
mixed case for ease of readability.
Example: ACCOUNTINGImportDailyFiles

SQL Server Security


This section contains advice on how to better secure SQL Server against malicious attacks by
outside resources.
Sql Injection
SQL Injection is a term describing the act of passing SQL code into an application that was
not intended by the developer. SQL injection usually occurs when developers use stringbinding (dynamic SQL) techniques in order to execute SQL code in a stored procedure. The
example below shows how SQL Injection can be used to execute malicious code.
Create procedure usp_get_county @input varchar(30)
As
Declare @sqltext varchar(7000)
Set @sqltext = select nam_county from e_county where cde_county = + @input +
Execute (@sqltext)
In the code example above, when used correctly, the procedure would return the name of the
county associated to the given cde_county.
Exec usp_get_county @input = 02
However, should the following line be passed instead, the code would allow a delete of the
table, clearly not what the developer had intended to happen.
Exec usp_get_county '''drop table e_county--'
Notice, in the line being passed in by intruder the extra quote(), this is classic example of
injection. By enclosing an extra single quote (), the dynamic original t-sql statement is ended
and a second statement sent to the exec. The Exec command now runs two statements that it
assumes are in batch. Also, notice the use of (--) to force sql server to ignore the trailing quote
placed by the developers code.
Depending on functions of stored procedure, privileges of user results can be drastic.
To avoid SQL injection the following methods are suggested:
1. When using a numeric variable, verify the data using the isnumeric function, and

MS SQL Server 2008 Naming and Coding Standard.doc

Page 36 of 37

2. Use the replace function to turn a single quote into two single quotes.
Ex. set @input = replace(@input,'''','''''')
By doing this we now insure that the developers intended code will run and the intruders code
will fail. If possible, the addition of an audit routine should be added to the procedure/
database to track when SQL Injection is attempted.

Exemptions from this Standard:


There will be no exemptions to this standard.
Refresh Schedule:
All standards and referenced documentation identified in this standard will be subject to review
and possible revision annually or upon request by the DPW Information Technology Standards
Team.
References:
Microsoft SQL Server 2008 Books On Line
Standard Revision Log:
Change
Date

Version

Change Description

Author and
Organization

1.0

New document

Kiley Milakovic

12/11/2007

1.1

SQL Server 2005 revisions

Matt Leitzel

01/13/2011

1.2

SQL Server 2008 revisions

Matt Leitzel

06/20/2012

1.2

This document is a revision of


Microsoft SQL Development
Standards, and replaces it. The
06/20/2012 date issue reflects the
issuing of a standard number and of
the name change.

Rich Gill

MS SQL Server 2008 Naming and Coding Standard.doc

Page 37 of 37

You might also like