SQL Server Performance Tuning Tips

In this article, you will learn the tips of fine tuning the performance of SQL Server.

Database is the most important and powerful part of any application. If your database is not working properly and taking long time to compute the result, this means something is going wrong in database. Here, database tune up is required, otherwise performance of the application will degrade.
 
Tuning 
 
I know a lot of articles already published on this topic. But in this article, I tried to provide a list of database tune up tips that will cover all the aspects of database. Database tuning is a very critical and fussy process. It is true that database tuning is a database admin task but we should have the basic level of knowledge for doing this. Because, if we are working on a project where there is no role of admin, then it is our responsibility to maintain the performance of the database. If performance of database is degraded, then it will cause the worst effect on the whole system. In this article, I will explain some basic database tuning tips that I learned from my experience and from my friends who are working as database administrator. Using these tips, you can maintain or upgrade the performance of your database system. Basically, these tips are written for SQL Server but we can implement these into another databases too, like Oracle and MySQL. Please read these tips carefully and at the end of article, let me know if you find something wrong or incorrect.
 

Avoid Null value in fixed length field

 
We should avoid the Null value in fixed length fields because if we insert the NULL value in a fixed length field, then it will take the same amount of space as the desired input value for that field. So, if we require a null value in a field, then we should use a variable length field that takes lesser space for NULL. The use of NULLs in a database can reduce the database performance, especially,  in WHERE clauses. For example, try to use varchar instead of char and nvarchar.
  1. Never use Select * Statement:  
When we require all the columns of a table, we usually use a “Select *” statement. Well, this is not a good approach because when we use the “select *” statement, the SQL Server converts * into all column names before executing the query, which takes extra time and efforts. So, always provide all the column names in the query instead of “select *”.
 

Normalize tables in database

 
Normalized and managed tables increase the performance of a database. So,  always try to perform at least 3rd normal form. It is not necessary that all tables require 3NF normalization form, but if any table contains 3NF form normalization, then it can be called well structured tables.
 

Keep Clustered Index Small

 
Clustered index stores data physically into memory. If the size of a clustered index is very large, then it can reduce the performance. Hence, a large clustered index on a table with a large number of rows increases the size significantly. Never use an index for frequently changed data because when any change in the table occurs, the index is also modified, and that can degrade performance.
 

Use Appropriate Datatype

 
If we select inappropriate data type, it will reduce the space and enhance the performance; otherwise, it generates the worst effect. So, select an appropriate data type according to the requirement. SQL contains many data types that can store the same type of data but you should select an appropriate data type because each data type has some limitations and advantages upon another one.
 

Store image path instead of image itself

 
I found that many developers try to store the image into database instead of the image path. It may be possible that it is requirement of application to store images into database. But generally, we should use image path, because storing image into database increases the database size and reduces the performance.
 

USE Common Table Expressions (CTEs) instead of Temp table

 
We should prefer a CTE over the temp table because temp tables are stored physically in a TempDB which is deleted after the session ends. While CTEs are created within memory. Execution of a CTE is very fast as compared to the temp tables and very lightweight too.
 

Use Appropriate Naming Convention

 
The main goal of adopting a naming convention for database objects is to make it easily identifiable by the users, its type, and purpose of all objects contained in the database. A good naming convention decreases the time required in searching for an object. A good name clearly indicates the action name of any object that it will perform.
  1. * tblEmployees // Name of table  
  2. * vw_ProductDetails // Name of View  
  3. * PK_Employees // Name of Primary Key  

Use UNION ALL instead of UNION

 
We should prefer UNION ALL instead of UNION because UNION always performs sorting that increases the time. Also, UNION can't work with text datatype because text datatype doesn't support sorting. So, in that case, UNION can't be used. Thus, always prefer UNION All.
 

Use Small data type for Index

 
It is very important to use Small data type for index. Because, the bigger size of data type reduces the performance of Index. For example, nvarhcar(10) uses  20 bytes of data and varchar(10) uses 10 bytes of the data. So, the index for varhcar data type works better. We can also take another example of datetime and int. Datetime data type takes 8 Bytes and int takes 4 bytes. Small datatype means less I/O overhead that increases the performance of the index.
  1. Use Count(1) instead of Count(*) and Count(Column_Name):  
There is no difference in the performance of these three expressions; but, the last two expressions are not well considered to be a good practice. So, always use count(10) to get the numbers of records from a table.
 

Use Stored Procedure

 
Instead of using the row query, we should use the stored procedure because stored procedures are fast and easy to maintain for security and large queries.
 

Use Between instead of In

 
If Between can be used instead of IN, then always prefer Between. For example, you are searching for an employee whose id is either 101, 102, 103 or 104. Then, you can write the query using the In operator like this:
  1. Select * From Employee Where EmpId In (101,102,103,104)  
You can also use Between operator for the same query.
  1. Select * from Employee Where EmpId Between 101 And 104  

Use If Exists to determine the record

 
It has been seen many times that developers use "Select Count(*)" to get the existence of records. For example
  1. Declare @Count int;  
  2. Set @Count=(Select * From Employee Where EmpName Like '%Pan%')  
  3. If @Count>0  
  4. Begin  
  5. //Statement  
  6. End  
But, this is not a proper way for such type of queries. Because, the above query performs the complete table scan, so you can use If Exists for same query. That will increase the performance of your query, as below.
  1. IF Exists(Select Emp_Name From Employee Where EmpName Like '%Pan%')  
  2. Begin  
  3. //Statements  
  4. End  

Never Use ” Sp_” for User Define Stored Procedure

 
Most programmers use “sp_” for user-defined Stored Procedures. I suggest to never use “sp_” for user-defined Stored Procedure because in SQL Server, the master database has a Stored Procedure with the "sp_" prefix. So, when we create a Stored Procedure with the "sp_" prefix, the SQL Server always looks first in the Master database, then in the user-defined database, which takes some extra time.
 

Practice to use Schema Name

 
A schema is the organization or structure for a database. We can define a schema as a collection of database objects that are owned by a single principal and form a single namespace. Schema name helps the SQL Server finding that object in a specific schema. It increases the speed of the query execution. For example, try to use [dbo] before the table name.
 

Avoid Cursors

 
A cursor is a temporary work area created in the system memory when a SQL statement is executed. A cursor is a set of rows together with a pointer that identifies the current row. It is a database object to retrieve the data from a result set one row at a time. But, use of a cursor is not good because it takes long time because it fetches data row by row. So, we can use a replacement of cursors. A temporary table for or While loop may be a replacement of a cursor in some case.
 

SET NOCOUNT ON

 
When an INSERT, UPDATE, DELETE or SELECT command is executed, the SQL Server returns the number affected by the query. It is not good to return the number of rows affected by the query. We can stop this by using NOCOUNT ON.
 

Use Try–Catch

 
In T-SQL, a Try-Catch block is very important for exception handling. A best practice and use of a Try-Catch block in SQL can save our data from undesired changes. We can put all T-SQL statements in a TRY BLOCK and the code for exception handling can be put into a CATCH block.
 

Remove Unused Index

 
Remove all unused indexes because indexes are always updated when the table is updated so the index must be maintained even if not used.
 

Always create index on table

 
An index is a data structure to retrieve fast data. Indexes are special lookup tables that the database search engine can use to speed up data retrieval. Simply an index is a pointer to data in a table. Mainly an index increases the speed of data retrieval. So always try to keep minimum one index on each table it may be either clustered or non-clustered index.
 

Use Foreign Key with appropriate action

 
A foreign key is a column or combination of columns that is the same as the primary key, but in a different table. Foreign keys are used to define a relationship and enforce integrity between two tables. In addition to protecting the integrity of our data, FK constraints also help document the relationships between our tables within the database itself. Also define an action rule for delete and update command, you can select any action among the No Action, Set NULL, Cascade and set default.
 

Use Alias Name

 
Aliasing renames a table or a column temporarily by giving another name. The use of table aliases means to rename a table in a specific
SQL statement. Using aliasing, we can provide a small name to large name that will save our time.
 

Use Transaction Management

 
A transaction is a unit of work performed against the database. A transaction is a set of work (T-SQL statements) that execute together like a single unit in a specific logical order as a single unit. If all the statements are executed successfully then the transaction is complete and the transaction is committed and the data will be saved in the database permanently. If any single statement fails then the entire transaction will fail and then the complete transaction is either cancelled or rolled back.
 

Use Index Name in Query

 
Although in most cases the query optimizer will pick the appropriate index for a specific table based on statistics, sometimes it is better to specify the index name in your SELECT query.
 
Example
  1. SELECT  
  2. e.Emp_IId,  
  3. e.First_Name,  
  4. e.Last_Name  
  5. FROM dbo.EMPLOYEE e  
  6. WITH (INDEX (Clus_Index))  
  7. WHERE e.Emp_IId > 5  
  8. Select Limited Data  
We should retrieve only the required data and ignore the unimportant data. The less data retrieved, the faster the query will run. Rather than filtering on the client, push as much filtering as possible on the server-end. This will result in less data being sent on the wire and you will see results much faster.
 

Drop Index before Bulk Insertion of Data

 
We should drop the index before insertion of a large amount of data. This makes the insert statement run faster. Once the inserts are completed, you can recreate the index again.
 

Use Unique Constraint and Check Constraint

 
A Check constraint checks for a specific condition before inserting data into a table. If the data passes all the Check constraints then the data will be inserted into the table otherwise the data for insertion will be discarded. The CHECK constraint ensures that all values in a column satisfies certain conditions.
 
A Unique Constraint ensures that each row for a column must have a unique value. It is like a Primary key but it can accept only one null value. In a table one or more column can contain a Unique Constraint. So we should use a Check Constraint and Unique Constraint because it maintains the integrity in the database.
 

Importance of Column Order in index

 
If we are creating a Non-Clustered index on more than one column then we should consider the sequence of the columns. The order or position of a column in an index also plays a vital role in improving SQL query performance. An index can help to improve the SQL query performance if the criteria of the query matches the columns that are left most in the index key. So we should place the most selective column at left most side of a non-clustered index.
 

Recompiled Stored Procedure

 
We all know that Stored Procedures execute T-SQL statements in less time than the similar set of T-SQL statements are executed individually. The reason is that the query execution plan for the Stored Procedures are already stored in the "sys.procedures" system-defined view. We all know that recompilation of a Stored Procedure reduces SQL performance. But in some cases it requires recompilation of the Stored Procedure. Dropping and altering of a column, index and/or trigger of a table. Updating the statistics used by the execution plan of the Stored Procedure. Altering the procedure will cause the SQL Server to create a new execution plan.
 

Use Sparse Column

 
Sparse column provide better performance for NULL and Zero data. If you have any column that contain large amount numbers of NULL and Zero then prefer Sparse Column instead of default column of SQL Server. Sparse column take lesser space then regular column (without SPARSE clause).
 
Example
  1. Create Table Table_Name  
  2. (  
  3. Id int, //Default Column  
  4. Group_Id int Sparse // Sparse Column  
  5. )  

Avoid Loops In Coding

 
Suppose you want to insert 10 of records into table then instead of using a loop to insert the data into table you can insert all data using single insert query.
  1. declare @int int;  
  2. set @int=1;  
  3. while @int<=10  
  4. begin  
  5. Insert Into Tab values(@int,'Value'+@int);  
  6. set @int=@int+1;  
  7. end  
Above method is not a good approach to insert the multiple records instead of this you can use another method like below.
  1. Insert Into Tab values(1,'Value1'),(2,'Value2'),(3,'Value3'),(4,'Value4'),(5,'Value5'),(6,'Value6'),(7,'Value7'),(8,'Value8'),(9,'Value9'),(10,'Value10');  

Avoid Correlated Queries

 
In A Correlated query inner query take input from outer(parent) query, this query run for each row that reduces the performance of database.
  1. Select Name, City, (Select Company_Name  
  2. from  
  3. Company where companyId=cs.CustomerId) from Customer cs  
The best method is that we should prefer the join instead of correlated query as below.
  1. Select cs.Name, cs.City, co.Company_Name  
  2. from Customer cs  
  3. Join  
  4. Company co  
  5. on  
  6. cs.CustomerId=co.CustomerId  

Avoid index and join hints

 
In some cases, index and join hint may increase the performance of database, but if you provide any join or index hint then server always try to use the hint provided by you although it has a better execution plan, so such type of approach may reduce the database performance. Use Join or index hint if you are confident that there is not any better execution plan. If you have any doubt then make server free to choose an execution plan.
 

Avoid Use of Temp table

 
Avoid use of temp table as much as you can because temp table is created into temp database like any basic table structure. After completion of the task, we require to drop the temp table. That rises the load on database. You can prefer the table variable instead of this.
 

Use Index for required columns

 
Index should be created for all columns which are using Where, Group By, Order By, Top, and Distinct command.
 

Don't use Index

 
It is true that use of index makes the fast retrieval of the result. But, it is not always true. In some cases, the use of index doesn't affect the performance of the query. In such cases, we can avoid the use of index.
  1. When size of table is very small.
  2. Index is not used in query optimizer
  3. DML(insert, Update, Delete) operation are frequent used.
  4. Column contain TEXT, nText type of data.

Use View for complex queries

 
If you are using join on two or more tables and the result of queries is frequently used, then it will be better to make a View that will contain the result of the complex query. Now, you can use this View multiple times, so that you don't have to execute the query multiple times to get the same result.
 

Make Transaction short

 
It will be better to keep the transaction as short as we can. Because, big size of transaction makes the table locked and reduces the database concurrency. So, always try to make shorter transactions.
 

Use Full-text Index

 
If your query contains multiple wild card searches using LIKE(%%), then use of Full-text Index can increase the performance. Full-text queries can include simple words and phrases or multiple forms of a word or phrase. A full-text query returns any document that contains at least one match (also known as a hit). A match occurs when a target document contains all the terms specified in the Full-text query, and meets any other search conditions, such as the distance between the matching terms.
 
Thanks for reading the article. As I have asked in the starting, if you have any doubt or I wrote something wrong, then write me back in comments section.