Tuesday, 30 June 2015

sql-server-stored-procedure-optimization-tips-best-practices/

SQL SERVER – Stored Procedure Optimization Tips – Best Practices

We will go over how to optimize Stored Procedure with making simple changes in the code. Please note there are many more other tips, which we will cover in future articles.
  • Include SET NOCOUNT ON statement:With every SELECT and DML statement, the SQL server returns a message that indicates the number of affected rows by that statement. This information is mostly helpful in debugging the code, but it is useless after that. By setting SET NOCOUNT ON, we can disable the feature of returning this extra information. For stored procedures that contain several statements or contain Transact-SQL loops, setting SET NOCOUNT to ON can provide a significant performance boost because network traffic is greatly reduced.
CREATE PROCdbo.ProcName
AS
SET NOCOUNT ON;
--Procedure code here
SELECT column1 FROMdbo.TblTable1
-- Reset SET NOCOUNT to OFF
SET NOCOUNT OFF;
GO

  • Use schema name with object name: The object name is qualified if used with schema name. Schema name should be used with the stored procedure name and with all objects referenced inside the stored procedure. This help in directly finding the complied plan instead of searching the objects in other possible schema before finally deciding to use a cached plan, if available. This process of searching and deciding a schema for an object leads to COMPILE lock on stored procedure and decreases the stored procedure’s performance. Therefore, always refer the objects with qualified name in the stored procedure like
SELECT FROMdbo.MyTable -- Preferred method
-- Instead of
SELECT FROM MyTable -- Avoid this method
--And finally call the stored procedure with qualified name like:
EXEC dbo.MyProc -- Preferred method
--Instead of
EXEC MyProc -- Avoid this method

  • Do not use the prefix “sp_” in the stored procedure name: If a stored procedure name begins with “SP_,” then SQL server first searches in the master database and then in the current session database. Searching in the master database causes extra overhead and even a wrong result if another stored procedure with the same name is found in master database.
  • Use IF EXISTS (SELECT 1) instead of (SELECT *): To check the existence of a record in another table, we uses the IF EXISTS clause. The IF EXISTS clause returns True if any value is returned from an internal statement, either a single value “1” or all columns of a record or complete recordset. The output of the internal statement is not used. Hence, to minimize the data for processing and network transferring, we should use “1” in the SELECT clause of an internal statement, as shown below:
IF EXISTS (SELECT 1FROM sysobjects
WHERE name 'MyTable'AND type 'U')

  • Use the sp_executesql stored procedure instead of the EXECUTE statement.
    The sp_executesql stored procedure supports parameters. So, using the sp_executesql stored procedure instead of the EXECUTE statement improve the re-usability of your code. The execution plan of a dynamic statement can be reused only if each and every character, including case, space, comments and parameter, is same for two statements. For example, if we execute the below batch:
DECLARE @QueryVARCHAR(100)
DECLARE @Age INT
SET @Age 25
SET @Query ='SELECT * FROM dbo.tblPerson WHERE Age = ' +CONVERT(VARCHAR(3),@Age)
EXEC (@Query)

If we again execute the above batch using different @Age value, then the execution plan for SELECT statement created for @Age =25 would not be reused. However, if we write the above batch as given below,
DECLARE @QueryNVARCHAR(100)
SET @Query =N'SELECT * FROM dbo.tblPerson WHERE Age = @Age'
EXECUTE sp_executesql@QueryN'@Age int',@Age 25

the compiled plan of this SELECT statement will be reused for different value of @Age parameter. The reuse of the existing complied plan will result in improved performance.
  • Try to avoid using SQL Server cursors whenever possible: Cursor uses a lot of resources for overhead processing to maintain current record position in a recordset and this decreases the performance. If we need to process records one-by-one in a loop, then we should use the WHILE clause. Wherever possible, we should replace the cursor-based approach with SET-based approach. Because the SQL Server engine is designed and optimized to perform SET-based operation very fast. Again, please note cursor is also a kind of WHILE Loop.
  • Keep the Transaction as short as possible: The length of transaction affects blocking and deadlocking. Exclusive lock is not released until the end of transaction. In higher isolation level, the shared locks are also aged with transaction. Therefore, lengthy transaction means locks for longer time and locks for longer time turns into blocking. In some cases, blocking also converts into deadlocks. So, for faster execution and less blocking, the transaction should be kept as short as possible.
  • Use TRY-Catch for error handling: Prior to SQL server 2005 version code for error handling, there was a big portion of actual code because an error check statement was written after every t-sql statement. More code always consumes more resources and time. In SQL Server 2005, a new simple way is introduced for the same purpose. The syntax is as follows:
BEGIN TRY
--Your t-sql code goes here
END TRY
BEGIN CATCH
--Your error handling code goes here
END CATCH

Tuesday, 23 June 2015

Execution Order of Triggers In SQL

Triggers are stored programs that are automatically executed or fired when a specified event occurs. It is a database object that is bound to a table and is executed automatically. We cannot call triggers explicitly. Triggers provide data integrity and are used to access and check data before and after modification using DDL or DML queries.

Triggers are used mainly in the following events:
  1. Insert Data into table
  2. Delete data from table
  3. Update table record
We can create more than one trigger for the same event (in other words an INSERT, DELETE, UPDATE transaction). These is one problem, however. Triggers don't have a specified execution order. Execution of triggers are performed randomly. Sometimes the business logic dictates that we need to define two triggers on a table that must fire in a specific order on the same table action. For example when we insert rows in a table (INSERT statement) two triggers must fire and the second must fire after the first one for our logic to be implemented correctly.

Today we learn how to define the execution order of triggers.

First we create a table as in the following:
  1. GO  
  2.   
  3. CREATE TABLE [dbo].[Employee](  
  4.     [Emp_ID] [intNOT NULL,  
  5.     [Emp_Name] [nvarchar](50) NOT NULL,  
  6.     [Emp_Salary] [intNOT NULL,  
  7.     [Emp_City] [nvarchar](50) NOT NULL,  
  8.  CONSTRAINT [PK_Employee] PRIMARY KEY CLUSTERED   
  9. (  
  10.     [Emp_ID] ASC  
  11. )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ONON [PRIMARY]  
  12. ON [PRIMARY]  
  13.   
  14. GO  
Now insert some values into the table.
  1. Insert into Employee  
  2. Select 1,'Pankaj',25000,'Alwar' Union All  
  3. Select 2,'Rahul',26000,'Alwar' Union All  
  4. Select 3,'Sandeep',25000,'Alwar' Union All  
  5. Select 4,'Sanjeev',24000,'Alwar' Union All  
  6. Select 5,'Neeraj',28000,'Alwar' Union All  
  7. Select 6,'Naru',20000,'Alwar' Union All  
  8. Select 7,'Omi',23000,'Alwar'   
Select all the values from the table.

table

Now we create two triggers for the insert event.

Example 1

  1. CREATE TRIGGER TRIGGER_SECOND  
  2. ON Employee  
  3. AFTER INSERT  
  4.   
  5. AS  
  6. BEGIN  
  7.   
  8.     PRINT ' MY EXECUTE ORDER IS SECOND'  
  9. END  
Now create a another trigger.
  1. CREATE  TRIGGER TRIGGER_FIRST  
  2. ON Employee  
  3. AFTER INSERT  
  4.   
  5. AS  
  6. BEGIN  
  7.   
  8.     PRINT ' MY EXECUTE ORDER IS FIRST'  
  9. END  
Now we insert data into the employee table.
  1. INSERT INTO Employee VALUES(11,'DIV',24000,'JAIPUR')  
Output 
    MY EXECUTE ORDER IS SECOND
    MY EXECUTE ORDER IS FIRST

    (1 row(s) affected)
We can see that the order of execution of the triggers may depend upon the order of their creation. By default, multiple triggers on a SQL Server table for the same action are not fired in a guaranteed order.

Now we learn how to define the execution order of triggers.

SQL Server contains a sp_settriggerorder Stored Procedure for defining the execution orders of triggers.

Syntax of sp_settriggerorder
  1. sp_settriggerorder [ @triggername = ] ‘[ triggerschema. ] triggername‘  
  2. , [ @order = ] ‘value‘  
  3. , [ @stmttype = ] ‘statement_type‘  
  4. [ , [ @namespace = ] { ‘DATABASE’ | ‘SERVER’ | NULL } ]   
A brief explanation of the arguments follows.

[ @triggername= ] '[ triggerschema.] triggername' : It defines the trigger name and schema name to which it belongs.

@order: defines the execution order of a trigger. The value is a varchar(10) and it can be any one of the following values.
 
ValueOrder
FirstExecute order is first
LastExecution order is last
NoneExecution order is undefined
@stmttype: defines the type of trigger, whether insert, delete or update trigger, LOGON, or any Transact-SQL statement event listed in DDL Events. 

@namespace: SQL Server 2005 specific and indicates whether a DDL trigger was created on the database or on the server. If set to NULL, it indicates that the trigger is a DML trigger.

Now to see some examples.

Example 2
In the preceding example we create two triggers, TRIGGER_FIRST and TRIGGER_SECOND. Now we define the order of both triggers.

First we set the order of TRIGGER_FIRST.
  1. EXEC sys.sp_settriggerorder @triggername = 'TRIGGER_FIRST',  
  2.    @order = 'FIRST',  
  3.    @stmttype = 'INSERT',  
  4.    @namespace = NULL  
Now we set the order of TRIGGER_SECOND.
  1. EXEC sys.sp_settriggerorder @triggername = 'TRIGGER_SECOND',  
  2.    @order = 'LAST',  
  3.    @stmttype = 'INSERT',  
  4.    @namespace = NULL  
After defining the order of execution now we insert some data into the table and examine the result.
  1. INSERT INTO Employee  
  2.     VALUES (10, 'DEV', 25000, 'JAIPUR')  
Output
    MY EXECUTE ORDER IS FIRST
    MY EXECUTE ORDER IS SECOND

    (1 row(s) affected)
As we expect, trigger TRIGGER_FIRST executes first then trigger TRIGGER_SECOND executes.

Example 3

Now we create 2 more triggers and define their order.
  1. CREATE  TRIGGER TRIGGER_FOURTH  
  2. ON Employee  
  3. AFTER INSERT  
  4.   
  5. AS  
  6. BEGIN  
  7.   
  8.     PRINT ' MY EXECUTE ORDER IS FOURTH'  
  9. END  
And
  1. CREATE  TRIGGER TRIGGER_THIRD  
  2. ON Employee  
  3. AFTER INSERT  
  4.   
  5. AS  
  6. BEGIN  
  7.   
  8.     PRINT ' MY EXECUTE ORDER IS THIRD'  
  9. END  
Now we define the orders of these two triggers.
  1. EXEC sys.sp_settriggerorder @triggername = 'TRIGGER_FOURTH',  
  2.    @order = 'NONE',  
  3.    @stmttype = 'INSERT',  
  4.    @namespace = NULL  
  5.   
  6. EXEC sys.sp_settriggerorder @triggername = 'TRIGGER_THIRD',  
  7.    @order = 'NONE',  
  8.    @stmttype = 'INSERT',  
  9.    @namespace = NULL  
Now insert some data into the table and examine the result.
  1. INSERT INTO Employee  
  2. VALUES (10, 'DEV', 25000, 'JAIPUR')  
Output
    MY EXECUTE ORDER IS FIRST
    MY EXECUTE ORDER IS FOURTH
    MY EXECUTE ORDER IS THIRD
    MY EXECUTE ORDER IS SECOND
We can see that order of TRIGGER_FIRST and TRIGGER_LAST is define but order of TRIGGER_THIRD and TRIGGER_FOURTH is not define so these both trigger execute in random order b/w TRIGGER_FIRST and TRIGGER_SECOND.

Example 4

Let us see another example.
  1. EXEC sys.sp_settriggerorder @triggername = 'TRIGGER_FIRST',  
  2.      @order = 'FIRST',  
  3.      @stmttype = 'INSERT',  
  4.      @namespace = NULL  
  5.   
  6. EXEC sys.sp_settriggerorder @triggername = 'TRIGGER_THIRD',  
  7.      @order = 'FIRST',  
  8.      @stmttype = 'INSERT',  
  9.      @namespace = NULL  
Output
 
Msg 15130, Level 16, State 1, Procedure sp_settriggerorder, Line 163
There already exists a 'FIRST' trigger for 'INSERT'.
 
When we run the preceding query SQL Server throws an error because we can't provide FIRST and LAST order to more than one trigger. If a first trigger is already defined on the table, database, or server, we cannot designate a new trigger as first for the same table, database, or server for the same statement type. This restriction also applies to last triggers.

Example 5
  1. SELECT  
  2.     sys.TABLES.name,  
  3.     sys.TRIGGERS.name,  
  4.     sys.TRIGGER_EVENTS.type,  
  5.     sys.TRIGGER_EVENTS.TYPE_DESC,  
  6.     IS_FIRST,  
  7.     IS_LAST,  
  8.     sys.TRIGGERS.CREATE_DATE,  
  9.     sys.TRIGGERS.MODIFY_DATE  
  10. FROM sys.TRIGGERS  
  11. INNER JOIN sys.TRIGGER_EVENTS  
  12.     ON sys.TRIGGER_EVENTS.object_id = sys.TRIGGERS.object_id  
  13. INNER JOIN sys.TABLES  
  14.     ON sys.TABLES.object_id = sys.TRIGGERS.PARENT_ID  
  15. ORDER BY MODIFY_DATE  
Output

Output