0% found this document useful (0 votes)
88 views

Best SQL Practices On Performance

This document provides guidelines for performance tuning a SQL Server 2008 R2 database and maintenance activities. It includes recommendations for tuning queries, analyzing slow queries, maintaining indexes, checking server resources, and optimizing stored procedures. A table of contents lists sections on tuning guidelines, slow query analysis, and various SQL maintenance scripts and checks.

Uploaded by

Prabu Jothi
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
88 views

Best SQL Practices On Performance

This document provides guidelines for performance tuning a SQL Server 2008 R2 database and maintenance activities. It includes recommendations for tuning queries, analyzing slow queries, maintaining indexes, checking server resources, and optimizing stored procedures. A table of contents lists sections on tuning guidelines, slow query analysis, and various SQL maintenance scripts and checks.

Uploaded by

Prabu Jothi
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 25

Performance Tuning & Maintenance Document Page 1 of 25

Performance Tuning
DB SQL Server 2008 R2
&
DB Maintenance Document














Performance Tuning & Maintenance Document Page 2 of 25

Revision History


Ver. # Description of
change
Sections Affected Created/
Modified By
Approved
By
Date of
issue
0.1 Initial Draft Alagarsamy T 23-Jan-13













Performance Tuning & Maintenance Document Page 3 of 25

Table of Contents
1 DB PERFORMANCE TUNING GUIDELINES ....................................................................................................... 4
2 CHECKLIST FOR ANALYZING SLOW-RUNNING QUERIES ............................................................................... 11
3 SQL MAINTENANCE ACTIVITY ...................................................................................................................... 12
3.1 TO CHECK THE MEMORY STATUS ........................................................................................................................ 12
3.2 TO CHECK THE UNUSED CACHE ........................................................................................................................... 12
3.3 TO PULL THE PHYSICAL MEMORY & SERVER INFORMATION ....................................................................................... 12
3.4 TO CALCULATE THE TPM .................................................................................................................................. 12
3.5 TO CHECK THE CURRENT CONCURRENT USERS (PROCESSES AT THAT TIME). ................................................................. 13
3.6 TO CHECK THE RAM MEMORY .......................................................................................................................... 13
3.7 TO IDENTIFICATION OF UNUSED INDEX................................................................................................................. 14
3.8 DRIVEN QUERIES (SAMPLE) ............................................................................................................................... 15
3.9 TABLE BUFFER USAGE ...................................................................................................................................... 15
3.10 RE-INDEXING.................................................................................................................................................. 17
3.11 REBUILD THE INDEX ......................................................................................................................................... 18
3.12 SCRIPTS FOR DEFRAGMENTATION OF THE INDEX .................................................................................................... 19
3.13 SCRIPTS FOR GENERATING DROP & CREATE INDEX ................................................................................................. 21
3.14 TO GET THE AGGREGATE PERFORMANCE STATISTICS FROM PLAN CACHE ..................................................................... 24















Performance Tuning & Maintenance Document Page 4 of 25

1 DB Performance Tuning Guidelines

1. Do not use OPENROWSET and OLEDB command anywhere in the SQL code.

2. For the bulk column fetch, first retrieve the main field based on the different condition and then
fetch all required columns based on the derived main fields.
a. E.g. In field status, first fetch the Work Orders & then fetch the details of Work Orders
b. Similarly, fetch the Job Id first in Technician Job Details & then fetch all the other
columns based on the Job id which was retrieved initially.

3. Always do not fetch all the grids in a screen. Analyse the screen and based on the result,
proceed for coding
a. E.g. In field status, there are three grids where we can display the output for
1. Job Summary
2. List View
3. Total Count
b. In this case, Job Summary & Total Count outputs will not vary unless we modify the
main input filter conditions.
c. For sorting any columns in the list view, we should not load the Job Summary & Total
Count columns again, since they would have loaded already. (UI changes are needed)
d. Based on the sorting, we can load only the List view.

4. Avoid unwanted where condition while framing SQL Query dynamically
a. E.g, if the Order Date input is passed as NULL, then we should not send any filter for
Order Date.

Donts:
and wo.priority_id = isnull(pi_priority_id,wo.priority_id)

Dos:
if @pi_priority_id is not null
begin
select @l_str = @l_str + ' and wo.priority_id =
'+convert(nvarchar(200),@pi_priority_id) +' '
end
5. Do not use functions in the left side of the where condition
a. E.g. where isnull(order_date,) between @pi_start and @pi_end
b. where cast(order_date as date) between @pi_start and @pi_end
c. where dbo.fn_Xdate(order_date) between @pi_start and @pi_end



Performance Tuning & Maintenance Document Page 5 of 25

6. All the images should be stored in Fileserver / Filestream. Also it would be better if it is read
from UI side.

7. Avoid using functions in where clause.
Donts:
and isnull(reviewer_id,0) = isnull(@pi_reviewer_id,0)

Dos:
and (reviewer_id is not null and reviewer_id = isnull(@pi_reviewer_id,0))

8. Join the different tables based on the volume from small to large
Donts:
from #Work_order_Result wo
join work_order wo1 (nolock) on wo1.work_order_id= wo.work_order_id

Dos:

From #work_order_result wo
join work_order wo1 (nolock) on wo.work_order_id= wo1.work_order_id

9. In a Join, keep inner join first, after that keep all the left join , It will reduce the IO Reads.
Donts:

from #Work_order_Result wo1
Join ref_work_item_type rwit (nolock)
on rwit.work_item_type_id = wo1.work_item_type_id
left join ref_organization ro (nolock) on ro.id_org = wo1.vendor_id
left join ref_priority rp (nolock) on rp.priority_id= wo1.priority_id
join vw_property_details re on re.Property_Id = wo1.Property_Id

Dos:

from #Work_order_Result wo1
Join ref_work_item_type rwit (nolock)
on rwit.work_item_type_id = wo1.work_item_type_id
join vw_property_details re on re.Property_Id = wo1.Property_Id
left join ref_organization ro (nolock) on ro.id_org = wo1.vendor_id
left join ref_priority rp (nolock) on rp.priority_id= wo1.priority_id



10. Use Nolock for well known masters for fetching stored procedures.
Donts:



Performance Tuning & Maintenance Document Page 6 of 25

from #Work_order_Result wo1
join ref_priority rp on rp.priority_id= wo1.priority_id
join vw_property_details re on re.Property_Id = wo1.Property_Id

Dos:

from #Work_order_Result wo1
join ref_priority rp (nolock) on rp.priority_id= wo1.priority_id
join vw_property_details re (noexpand) on re.Property_Id= wo1.Property_Id

11. Use Periodic Index, instead of reindex. With the help of Show Index Statistics, we should use
index defragment.

Eg:
USE FPREO;
GO
DBCC SHOW_STATISTICS ("ref_meta_data", pk_ref_meta_data_meta_data_id);


12. Always use covering index for non-clustered index.

Donts:
CREATE NONCLUSTERED INDEX IX_OrderDetailDateProdSold ON dbo.OrderDetail
( ProductID, OrderDate)

Dos:
CREATE NONCLUSTERED INDEX IX_OrderDetailDateProdSold ON dbo.OrderDetail
( ProductID, OrderDate) INCLUDE (QtySold);

13. If there is an index for any column, then check whether the read and write process are
performed by the respective index. It the read process does not happen for a long period, then
we do not need that index.

14. Try to have views for addresses, since we need to map many tables to fetch the address in
various queries. Please create an index for the respective views.


15. Also try to have views for Job details like Category, Service Type, Service Request in order to
avoid the joins for different tables in many queries.

16. Test every Execute Statements with the option with recompile

Eg: exec get_record_search_list_FS @pi_sp_name=N'[get_order_status]',


Performance Tuning & Maintenance Document Page 7 of 25

@sp_in_params=N'"null~null~null~null~null~null~null~null~null~Null~Null~Null~0, 5, 11, 12, 7,
4, 3, 2, 6, 9, 47, 8, 10~4~Null~null~null~null~1~null~null~null~1~Null~1~1"'
With Recompile


17. For every SP execution time , we need to verify the IO. Depending upon the IO , CPU Utilization
will differ.
Ex: Set Statistics IO on
EXEC SP Name
Set Statistics IO OFF

Logical read should be always less. Index for specific columns will help to reduce logical
Reads.

18. If a condition contains OR, then put that condition first
a. Where (wa.tech_id = @pi_tech_id or @pi_login_id = 1)
b. Please do write the above code as where (@pi_login_id = 1 or wa.tech_id =
@pi_tech_id)

19. If N select queries are mentioned in a SP, try to convert all the select queries into different SPs.

20. Remove Not in condition from all coding.
Donts:
and state_id not in (31,32,33,34)

Dos:
and state_id in (25,26,27,28)


21. Avoid use distinct key word

22. Create a function with SCHEMABINDING
Ex:
CREATE FUNCTION SchemaBinded(@INPUT INT)
RETURNS INT WITH SCHEMABINDING
BEGIN
RETURN @INPUT * 2 + 50
END
GO

http://www.mssqltips.com/sqlservertip/1692/using-schema-binding-to-improve-sql-server-udf-
performance/


23. To Reduce the IO of each stored procedures with the help of user defined function (use
Computed Columns /persisted Columns )



Performance Tuning & Maintenance Document Page 8 of 25

Ex:
alter table work_order add wo_category_id as
[dbo].[fn_wo_category_id](work_order_id) persisted
go

alter table work_order add wo_category_id as
[dbo].[fn_wo_category_id](work_order_id)
go - this is better than the above persited column

It will help to avoid Demoralization of the table.

24. Without Group by we will be able to get the No. of records count(*) Using
count(*) over( partition by (select 1)) as Total_Count
Record Count in the specific select statement itself.


25. XML is always faster than the CSV command. It will not have parsing methodology.
Ex:
declare @l_category nvarchar(max)

select @l_category =
substring(category ,1,len(category) -1)
from (
select
( select
convert(nvarchar(1000),wj1.work_order_category_
id ) + ','
from dbo.wo_job wj1 (nolock)
where wj1.work_order_id = wj.work_order_id
order by work_order_id
for xml path('') ) as category
from dbo.wo_job wj
where work_order_id = @pi_work_order_id
)a


26. Always use EXISTS query instead of NOT EXISTS.

Donts:
and not exists(select 1 from work_order wo1 (nolock)
where wo.work_order_id = wo1.work_order_id
and wo1.reviewer_id = @pi_login_id
)

Dos:
and exists(select 1 from work_order wo1 (nolock)
where wo.work_order_id = wo1.work_order_id
and wo1.reviewer_id = @pi_login_id )

27. If temporary tables are used in stored procedures, then drop temp table at the end of the sp.

Ex:


Performance Tuning & Maintenance Document Page 9 of 25

IF OBJECT_ID('tempdb..#temp_login') IS NOT NULL DROP TABLE #temp_login


28. Avoid using sub queries / joins with the same table in a stored procedure. Instead use EXISTS
which will improve the performance drastically.

Donts:
join ref_property re (nolock)
join ref_property re1 (nolock) on re1.property_id= re.property_id
and re1.property_stage_id in (1,2)
where re.property_id = @pi_property_id

Dos:
join ref_property re (nolock)
where re.property_id = @pi_property_id
and exists (select 1 ref_property re1 (nolock)
where re1.property_id = re.property_id
and re1.property_stage_id in (1,2)
)


29. Avoid using global temporary ##temp tables in a stored procedures. It will not support when
concurrent users perform simultaneously. i.e., same value will be passed to all users which leads
to wrong data entry.

Donts:
create table ##temp
( sl_no int identity(1,1),
property_id int
)

Dos:
create table #temp
( sl_no int identity(1,1),
property_id int
)

30. Avoid using SELECT INTO keyword for creating temp tables. Instead, create a temporary table
and then insert the records.

Donts:
Select id,designation
into #temp
from ref_designation
where is_active = 1


Dos:
Create table #temp (id bigint, designation nvarchar(400))



Performance Tuning & Maintenance Document Page 10 of 25

Insert into #temp (id, desigantion)
Select id,designation from ref_designation (nolock) where is_active = 1
IF OBJECT_ID('tempdb..#temp') IS NOT NULL DROP TABLE #temp

31. While insertion, use column list in the insert statement. It will avoid unwanted errors when a
new column is added in the specified table.

Donts:
Insert into #temp
Select work_order_id, work_order_status_work_item_type
from work_order where work_order_id = @pi_work_order_id

Dos:
Insert into #temp (work_order_id, work_order_status_work_item_type)
Select work_order_id, work_order_status_work_item_type
from work_order (nolock) where work_order_id = @pi_work_order_id

32. Use NOEXPAND keyword, when views are used in joins.

EX:
from #Work_order_Result wo1
join ref_priority rp (nolock) on rp.priority_id= wo1.priority_id
join vw_property_details re (noexpand) on re.Property_Id= wo1.Property_Id


33. Avoid selecting records by using * keyword in stored procedures. Instead, provide the column
list which needs to be given as output.

Donts:
Select * from ref_property (nolock) where property_id = @pi_property_id

Dos:
Select property_id, property_code, property_number,priority_id,is_active
from ref_property (nolock) where property_id = @pi_property_id





34. Instead of including Nolock for all the tables in the stored procedure, we can set the isolation level
as SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED in the top of stored procedures.


Performance Tuning & Maintenance Document Page 11 of 25

It will play the exact role of Nolock .It refers all the tables which referred in the respective stored
procedure. It would be applicable for the connections (Session ID) even though the respective Stored
procedures contains the nested Stored procedures/Remote Procedure. This should applicable for only
fetch stored procedures.

2 Checklist for Analyzing Slow-Running Queries

Slow network communication.
Inadequate memory in the server computer, or not enough memory available for SQL Server.
Lack of useful statistics
Lack of useful indexes.
Lack of useful indexed views.
Lack of useful data striping.
Lack of useful partitioning.
Reference: http://msdn.microsoft.com/en-us/library/ms177500.aspx


Performance Tuning & Maintenance Document Page 12 of 25

3 SQL Maintenance Activity

3.1 To Check the memory Status

DBCC MEMORYSTATUS
3.2 To Check the unused cache

DBCC FREESYSTEMCACHE ('ALL') WITH MARK_IN_USE_FOR_REMOVAL;

3.3 To pull the physical memory & server information

select * from sys.dm_os_sys_info
select * from sys.dm_os_sys_memory
select convert(numeric(5,2),(total_physical_memory_kb/1024.0/1024.0)) as
Total, convert(numeric(5,2),(available_physical_memory_kb/1024.0/1024.0))
as Available, system_memory_state_desc ,( Select
((bpool_committed*8)/1024.0/1024.0) from sys.dm_os_sys_info (nolock) ) As
SQLUseage from sys.dm_os_sys_memory (nolock)

3.4 To Calculate the TPM

DECLARE @cntr_value1 bigint
DECLARE @cntr_value2 bigint

t:

SELECT @cntr_value1 = cntr_value
FROM sys.dm_os_performance_counters
WHERE counter_name = 'transactions/sec'
AND object_name = 'SQLServer:Databases'


Performance Tuning & Maintenance Document Page 13 of 25

AND instance_name = 'FPREOPRO'

WAITFOR DELAY '00:00:01'

SELECT @cntr_value2 = cntr_value
FROM sys.dm_os_performance_counters
WHERE counter_name = 'transactions/sec'
AND object_name = 'SQLServer:Databases'
AND instance_name = 'FPREOPRO'
insert into mem_transaction_counter
Select @cntr_value2-@cntr_value1,getdate()
goto t
3.5 To check the current concurrent users (Processes at that time).

select GETDATE() as 'Time',COUNT(*) as 'Connection_count'
from master.dbo.sysprocesses p
join master.dbo.sysdatabases d on p.dbID = d.dbID
where p.dbid = db_id()

3.6 To check the RAM Memory

SELECT GETDATE() As Time, physical_memory_in_bytes/1073741824.0 as
[Physical Memory_GB] FROM sys.dm_os_sys_info



Performance Tuning & Maintenance Document Page 14 of 25


3.7 To Identification of unused Index
Scan: An index scan is a complete read of all of the leaf pages in the index.
Seek: An index seeks is an operation where SQL uses the b-tree structure to locate either a specific
value or the beginning of a range of value
If both are 0 then the index is useless.
1)
WITH indexstats ([Table],[Index],[Reads],[Writes],[Rows]) AS ( SELECT
usr.[name] + '.' + obj.[name] [Table], ixs.[name] [Index] ,
usage.user_seeks + usage.user_scans + usage.user_lookups [Reads],
usage.[user_updates] [Writes],
(SELECT SUM(sp.[rows]) FROM sys.partitions sp
WHERE usage.OBJECT_ID = sp.object_id AND sp.index_id = usage.index_id)
[Rows]
FROM sys.dm_db_index_usage_stats usage INNER JOIN sys.indexes ixs ON
usage.[object_id] = ixs.[object_id]
AND ixs.[index_id] = usage.[index_id] INNER JOIN sys.objects obj ON
usage.[object_id] = obj.[object_id]
INNER JOIN sys.sysusers usr ON obj.[schema_id] = usr.[uid] WHERE
usage.database_id = DB_ID()
AND usage.index_id > 0 AND OBJECTPROPERTY(usage.[object_id],
'IsUserTable') = 1 )
SELECT 'Drop Index ' +[INDEX] + ' on ' +[table],* FROM indexstats WHERE
Reads = 0 and ([index] not like '%pk_%' and [index] not like '%uk_%'
)ORDER BY [Rows] DESC, [Index]
go


2)
DECLARE @dbid INT
SELECT @dbid = DB_ID(DB_NAME())
SELECT OBJECTNAME = OBJECT_NAME(I.OBJECT_ID),
INDEXNAME = I.NAME,
I.INDEX_ID
FROM SYS.INDEXES I
JOIN SYS.OBJECTS O
ON I.OBJECT_ID = O.OBJECT_ID
WHERE OBJECTPROPERTY(O.OBJECT_ID,'IsUserTable') = 1
AND I.INDEX_ID NOT IN (
SELECT S.INDEX_ID
FROM SYS.DM_DB_INDEX_USAGE_STATS S
WHERE S.OBJECT_ID = I.OBJECT_ID
AND I.INDEX_ID = S.INDEX_ID
AND DATABASE_ID = @dbid)
ORDER BY OBJECTNAME,
I.INDEX_ID,
INDEXNAME ASC
GO




Performance Tuning & Maintenance Document Page 15 of 25

3.8 To Identification of Missing Index

SELECT mid.statement ,migs.avg_total_user_cost * (migs.avg_user_impact / 100.0) *
(migs.user_seeks + migs.user_scans) AS
improvement_measure,OBJECT_NAME(mid.Object_id),'CREATE INDEX [idx_' + CONVERT (varchar,
mig.index_group_handle) + '_' + CONVERT (varchar, mid.index_handle) + '_' +LEFT
(PARSENAME(mid.statement, 1), 32) + ']' + ' ON ' + mid.statement + ' (' + ISNULL
(mid.equality_columns,'') + CASE WHEN mid.equality_columns IS NOT NULL AND
mid.inequality_columns IS NOT NULL THEN ',' ELSE '' END + ISNULL (mid.inequality_columns,
'') + ')' + ISNULL (' INCLUDE (' + mid.included_columns + ')', '') AS
create_index_statement, migs.*, mid.database_id, mid.[object_id] FROM
sys.dm_db_missing_index_groups mig JOIN sys.dm_db_missing_index_group_stats migs ON
migs.group_handle = mig.index_group_handle JOIN sys.dm_db_missing_index_details mid ON
mig.index_handle = mid.index_handle WHERE migs.avg_total_user_cost *
(migs.avg_user_impact / 100.0) * (migs.user_seeks + migs.user_scans) > 10 ORDER BY
migs.avg_total_user_cost * migs.avg_user_impact * (migs.user_seeks + migs.user_scans)
DESC



3.9 Driven Queries (Sample)
Optimize Parameter Driven Queries with SQL Server OPTIMIZE FOR Hint:

DECLARE @Country VARCHAR(20)
SET @Country = 'US'

SELECT *
FROM Sales.SalesOrderHeader h, Sales.Customer c,
Sales.SalesTerritory t
WHERE h.CustomerID = c.CustomerID
AND c.TerritoryID = t.TerritoryID
AND CountryRegionCode = @Country
OPTION (OPTIMIZE FOR (@Country = 'US'))


Reference for the above:
http://www.mssqltips.com/sqlservertip/1354/optimize-parameter-
driven-queries-with-sql-server-optimize-for-hint/


3.10 Table Buffer Usage

SELECT
obj.[name],


Performance Tuning & Maintenance Document Page 16 of 25

i.[name],
i.[type_desc],
count(*)AS Buffered_Page_Count ,
count(*) * 8192.0 / (1024 * 1024) as Buffer_MB
FROM sys.dm_os_buffer_descriptors AS bd
INNER JOIN
(
SELECT object_name(object_id) AS name
,index_id ,allocation_unit_id, object_id
FROM sys.allocation_units AS au
INNER JOIN sys.partitions AS p
ON au.container_id = p.hobt_id
AND (au.type = 1 OR au.type = 3)
UNION ALL
SELECT object_name(object_id) AS name
,index_id, allocation_unit_id, object_id
FROM sys.allocation_units AS au
INNER JOIN sys.partitions AS p
ON au.container_id = p.hobt_id
AND au.type = 2
) AS obj
ON bd.allocation_unit_id = obj.allocation_unit_id
LEFT JOIN sys.indexes i on i.object_id = obj.object_id AND i.index_id =
obj.index_id
WHERE database_id = db_id()
GROUP BY obj.name, obj.index_id , i.[name],i.[type_desc]
ORDER BY Buffered_Page_Count DESC



Performance Tuning & Maintenance Document Page 17 of 25


3.11 Re-Indexing

CREATE PROCEDURE upd_reindex
AS
begin
set nocount on


declare @po_error_code nvarchar(4000),
@po_severity tinyint ,
@l_incr int ,
@l_count int ,
@l_table_name nvarchar(200)

--if exists(select 1 from sys.tables where name = 'TableRowCount')
--begin
-- drop table [TableRowCount]
--end


--CREATE TABLE [TableRowCount](
-- TableName sysname,
-- [TableRowCount] int )

----EXEC sp_MSForEachTable 'INSERT [TableRowCount](TableName,
[TableRowCount]) SELECT ''?'', COUNT(*) FROM ?'

--EXEC sp_MSforeachtable @command1 = "print '?' DBCC DBREINDEX ('?', '
', 80)"

--EXEC sp_updatestats

--select * from [TableRowCount]


create table #temp_reindex
(
sl_no int ,
table_name nvarchar(200)
)

insert into #temp_reindex
(
sl_no,
table_name
)
SELECT row_number() over(order by name),
name
FROM sys.tables
where type = 'U'

if exists (select 1 from #temp_reindex)


Performance Tuning & Maintenance Document Page 18 of 25

begin
select @l_incr = 1

select @l_count = COUNT(*) from #temp_reindex

while @l_incr < = @l_count
begin

select @l_table_name = table_name

from #temp_reindex tp
where sl_no = @l_incr

PRINT 'Reindexing Table: ' + @l_table_name
DBCC DBREINDEX(@l_table_name, '', 80)

select @l_incr = @l_incr + 1

select @l_table_name = null

end
end

EXEC sp_updatestats

set nocount off
end
go


3.12 Rebuild the Index

create procedure rebuild_index
as
begin
DECLARE @Database VARCHAR(255)
DECLARE @Table VARCHAR(255)
DECLARE @cmd NVARCHAR(500)
DECLARE @fillfactor INT

SET @fillfactor = 90

DECLARE DatabaseCursor CURSOR FOR
SELECT name FROM MASTER.dbo.sysdatabases
WHERE dbid = db_id()
ORDER BY 1

OPEN DatabaseCursor

FETCH NEXT FROM DatabaseCursor INTO @Database
WHILE @@FETCH_STATUS = 0
BEGIN



Performance Tuning & Maintenance Document Page 19 of 25

SET @cmd = 'DECLARE TableCursor CURSOR FOR SELECT ''['' +
table_catalog + ''].['' + table_schema + ''].['' +
table_name + '']'' as tableName FROM ' + @Database +
'.INFORMATION_SCHEMA.TABLES
WHERE table_type = ''BASE TABLE'''

-- create table cursor
EXEC (@cmd)
OPEN TableCursor

FETCH NEXT FROM TableCursor INTO @Table
WHILE @@FETCH_STATUS = 0
BEGIN

IF (@@MICROSOFTVERSION / POWER(2, 24) >= 9)
BEGIN
-- SQL 2005 or higher command
SET @cmd = 'ALTER INDEX ALL ON ' + @Table + ' REBUILD WITH
(FILLFACTOR = ' + CONVERT(VARCHAR(3),@fillfactor) + ')'
EXEC (@cmd)
END
ELSE
BEGIN
-- SQL 2000 command
DBCC DBREINDEX(@Table,' ',@fillfactor)
END

FETCH NEXT FROM TableCursor INTO @Table
END

CLOSE TableCursor
DEALLOCATE TableCursor

FETCH NEXT FROM DatabaseCursor INTO @Database
END
CLOSE DatabaseCursor
DEALLOCATE DatabaseCursor
end

3.13 Scripts for Defragmentation of the Index
create procedure defragment_index
as
begin

SET NOCOUNT ON;
DECLARE @tablename varchar(255);
DECLARE @execstr varchar(400);
DECLARE @objectid int;
DECLARE @indexid int;
DECLARE @frag decimal;
DECLARE @maxfrag decimal;

-- Decide on the maximum fragmentation to allow for.
SELECT @maxfrag = 30.0;


Performance Tuning & Maintenance Document Page 20 of 25


-- Declare a cursor.
DECLARE tables CURSOR FOR
SELECT TABLE_SCHEMA + '.' + TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = 'BASE TABLE';

-- Create the table.
CREATE TABLE #fraglist (
ObjectName char(255),
ObjectId int,
IndexName char(255),
IndexId int,
Lvl int,
CountPages int,
CountRows int,
MinRecSize int,
MaxRecSize int,
AvgRecSize int,
ForRecCount int,
Extents int,
ExtentSwitches int,
AvgFreeBytes int,
AvgPageDensity int,
ScanDensity decimal,
BestCount int,
ActualCount int,
LogicalFrag decimal,
ExtentFrag decimal);

-- Open the cursor.
OPEN tables;

-- Loop through all the tables in the database.
FETCH NEXT
FROM tables
INTO @tablename;

WHILE @@FETCH_STATUS = 0
BEGIN
-- Do the showcontig of all indexes of the table
INSERT INTO #fraglist
EXEC ('DBCC SHOWCONTIG (''' + @tablename + ''')
WITH FAST, TABLERESULTS, ALL_INDEXES, NO_INFOMSGS');
FETCH NEXT
FROM tables
INTO @tablename;
END;

-- Close and deallocate the cursor.
CLOSE tables;
DEALLOCATE tables;

-- Declare the cursor for the list of indexes to be defragged.
DECLARE indexes CURSOR FOR
SELECT ObjectName, ObjectId, IndexId, LogicalFrag


Performance Tuning & Maintenance Document Page 21 of 25

FROM #fraglist
WHERE LogicalFrag >= @maxfrag
AND INDEXPROPERTY (ObjectId, IndexName, 'IndexDepth') > 0;

-- Open the cursor.
OPEN indexes;

-- Loop through the indexes.
FETCH NEXT
FROM indexes
INTO @tablename, @objectid, @indexid, @frag;

WHILE @@FETCH_STATUS = 0
BEGIN
PRINT 'Executing DBCC INDEXDEFRAG (0, ' + RTRIM(@tablename) + ',
' + RTRIM(@indexid) + ') - fragmentation currently '
+ RTRIM(CONVERT(varchar(15),@frag)) + '%';
SELECT @execstr = 'DBCC INDEXDEFRAG (0, ' + RTRIM(@objectid) + ',
' + RTRIM(@indexid) + ')';
EXEC (@execstr);

FETCH NEXT
FROM indexes
INTO @tablename, @objectid, @indexid, @frag;
END;

-- Close and deallocate the cursor.
CLOSE indexes;
DEALLOCATE indexes;

-- Delete the temporary table.
DROP TABLE #fraglist;
end

3.14 Scripts for generating Drop & Create Index

SELECT
ixz.object_id,
tablename = QUOTENAME(scmz.name) + '.' +
QUOTENAME((OBJECT_NAME(ixz.object_id))),
tableid = ixz.object_id,
indexid = ixz.index_id,
indexname = ixz.name,
isunique = INDEXPROPERTY (ixz.object_id,ixz.name,'isunique'),
isclustered = INDEXPROPERTY (ixz.object_id,ixz.name,'isclustered'),
indexfillfactor = INDEXPROPERTY
(ixz.object_id,ixz.name,'indexfillfactor'),
--SQL2008+ Filtered indexes:
CASE
WHEN ixz.filter_definition IS NULL
THEN ''
ELSE ' WHERE ' + ixz.filter_definition
END Filter_Definition


Performance Tuning & Maintenance Document Page 22 of 25

--For 2005, which did not have filtered indexes, comment out the above
CASE statement, and uncomment this:
INTO #tmp_indexes
FROM sys.indexes ixz
INNER JOIN sys.objects obz
ON ixz.object_id = obz.object_id
INNER JOIN sys.schemas scmz
ON obz.schema_id = scmz.schema_id
WHERE ixz.index_id > 0
AND ixz.index_id < 255 ---- 0 = HEAP index, 255 = TEXT columns index
AND INDEXPROPERTY (ixz.object_id,ixz.name,'ISUNIQUE') = 0 -- comment out
to include unique and
AND INDEXPROPERTY (ixz.object_id,ixz.name,'ISCLUSTERED') = 0 -- comment
out to include PK's




ALTER TABLE #tmp_indexes ADD keycolumns VARCHAR(4000), includes
VARCHAR(4000)
GO

DECLARE @isql_key VARCHAR(4000),
@isql_incl VARCHAR(4000),
@tableid INT,
@indexid INT

DECLARE index_cursor CURSOR
FOR
SELECT
tableid,
indexid
FROM #tmp_indexes

OPEN index_cursor
FETCH NEXT FROM index_cursor INTO @tableid, @indexid

WHILE @@FETCH_STATUS <> -1
BEGIN
SELECT @isql_key = '', @isql_incl = ''
SELECT --ixz.name, colz.colid, colz.name, ixcolz.index_id,
ixcolz.object_id, *
--key column
@isql_key = CASE ixcolz.is_included_column
WHEN 0
THEN CASE ixcolz.is_descending_key
WHEN 1
THEN @isql_key + COALESCE(colz.name,'') + ' DESC, '
ELSE @isql_key + COALESCE(colz.name,'') + ' ASC, '
END
ELSE @isql_key
END,

--include column
@isql_incl = CASE ixcolz.is_included_column
WHEN 1


Performance Tuning & Maintenance Document Page 23 of 25

THEN CASE ixcolz.is_descending_key
WHEN 1
THEN @isql_incl + COALESCE(colz.name,'') + ', '
ELSE @isql_incl + COALESCE(colz.name,'') + ', '
END
ELSE @isql_incl
END
FROM sysindexes ixz
INNER JOIN sys.index_columns AS ixcolz
ON (ixcolz.column_id > 0
AND ( ixcolz.key_ordinal > 0
OR ixcolz.partition_ordinal = 0
OR ixcolz.is_included_column != 0)
)
AND ( ixcolz.index_id=CAST(ixz.indid AS INT)
AND ixcolz.object_id=ixz.id
)
INNER JOIN sys.columns AS colz
ON colz.object_id = ixcolz.object_id
AND colz.column_id = ixcolz.column_id
WHERE ixz.indid > 0 AND ixz.indid < 255
AND (ixz.status & 64) = 0
AND ixz.id = @tableid
AND ixz.indid = @indexid
ORDER BY
ixz.name,
CASE ixcolz.is_included_column
WHEN 1
THEN ixcolz.index_column_id
ELSE ixcolz.key_ordinal
END

--remove any trailing commas from the cursor results
IF LEN(@isql_key) > 1 SET @isql_key = LEFT(@isql_key, LEN(@isql_key) -1)
IF LEN(@isql_incl) > 1 SET @isql_incl = LEFT(@isql_incl, LEN(@isql_incl)
-1)
--put the columns collection into our temp table
UPDATE #tmp_indexes
SET keycolumns = @isql_key,
includes = @isql_incl
WHERE tableid = @tableid
AND indexid = @indexid
FETCH NEXT FROM index_cursor INTO @tableid,@indexid
END --WHILE
CLOSE index_cursor
DEALLOCATE index_cursor

--remove invalid indexes, ie ones without key columns
DELETE FROM #tmp_indexes WHERE keycolumns = ''

SET NOCOUNT ON

SELECT
'IF NOT EXISTS(SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'''
+ ti.TABLENAME + '''' + ') AND name = N' + '''' + ti.INDEXNAME + '''' +
')' + ' ' +


Performance Tuning & Maintenance Document Page 24 of 25

'CREATE '
+ CASE WHEN ti.ISUNIQUE = 1 THEN 'UNIQUE ' ELSE '' END
+ CASE WHEN ti.ISCLUSTERED = 1 THEN 'CLUSTERED ' ELSE '' END
+ 'INDEX ' + QUOTENAME(ti.INDEXNAME)
+ ' ON ' + (ti.TABLENAME) + ' '
+ '(' + ti.keycolumns + ')'
+ CASE
WHEN ti.INDEXFILLFACTOR = 0 AND ti.ISCLUSTERED = 1 AND INCLUDES = '' THEN
ti.Filter_Definition + ' WITH (SORT_IN_TEMPDB = ON) ON [' + fg.name + ']'
WHEN INDEXFILLFACTOR = 0 AND ti.ISCLUSTERED = 0 AND ti.INCLUDES = '' THEN
ti.Filter_Definition + ' WITH (ONLINE = ON, SORT_IN_TEMPDB = ON) ON [' +
fg.name + ']'
WHEN INDEXFILLFACTOR <> 0 AND ti.ISCLUSTERED = 0 AND ti.INCLUDES = ''
THEN ti.Filter_Definition + ' WITH (ONLINE = ON, SORT_IN_TEMPDB = ON,
FILLFACTOR = ' + CONVERT(VARCHAR(10),ti.INDEXFILLFACTOR) + ') ON [' +
fg.name + ']'
WHEN INDEXFILLFACTOR = 0 AND ti.ISCLUSTERED = 0 AND ti.INCLUDES <> ''
THEN ' INCLUDE (' + ti.INCLUDES + ') ' + ti.Filter_Definition + ' WITH
(ONLINE = ON, SORT_IN_TEMPDB = ON) ON [' + fg.name + ']'
ELSE ' INCLUDE(' + ti.INCLUDES + ') ' + ti.Filter_Definition + ' WITH
(FILLFACTOR = ' + CONVERT(VARCHAR(10),ti.INDEXFILLFACTOR) + ', ONLINE =
ON, SORT_IN_TEMPDB = ON) ON [' + fg.name + ']'
END
FROM #tmp_indexes ti
JOIN sys.indexes i ON ti.Object_id = i.object_id and ti.indexname =
i.name
JOIN sys.filegroups fg on i.data_space_id = fg.data_space_id
WHERE LEFT(ti.tablename,3) NOT IN ('sys', 'dt_') --exclude system tables
ORDER BY
ti.tablename,
ti.indexid,
ti.indexname

--makes the drop
SELECT
'DROP INDEX '
+ ' ' + (tablename) + '.'
+ (indexname) + ''
FROM #tmp_indexes
WHERE LEFT(tablename,4) NOT IN ('[sys', 'dt_')

----Drop the temp table again
DROP TABLE #tmp_indexes



3.15 To get the aggregate performance statistics from Plan Cache


SELECT total_logical_reads, total_logical_writes,
total_physical_reads, total_worker_time,
total_elapsed_time, sys.dm_exec_sql_text.TEXT
FROM sys.dm_exec_query_stats


Performance Tuning & Maintenance Document Page 25 of 25

CROSS APPLY sys.dm_exec_sql_text(plan_handle)
WHERE total_logical_reads <> 0
AND total_logical_writes <> 0
ORDER BY (total_logical_reads + total_logical_writes) DESC

You might also like