8/10/2019 Best SQL Practices on Performance
1/25
Performance Tuning & Maintenance Document Page 1 of 25
Performance Tuning
DB SQL Server 2008 R2
&
DB Maintenance Document
8/10/2019 Best SQL Practices on Performance
2/25
8/10/2019 Best SQL Practices on Performance
3/25
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 MEMORYSTATUS ........................................................................................................................ 12 3.2 TO CHECK THE UNUSED CACHE........................................................................................................................... 12 3.3 TO PULL THE PHYSICAL MEMORY& SERVER INFORMATION....................................................................................... 12 3.4 TO CALCULATE THETPM .................................................................................................................................. 12 3.5 TO CHECK THE CURRENT CONCURRENT USERS(PROCESSES AT THAT TIME). ................................................................. 13 3.6 TO CHECK THERAM MEMORY .......................................................................................................................... 13 3.7 TO IDENTIFICATION OF UNUSEDINDEX................................................................................................................. 14 3.8 DRIVENQ UERIES(SAMPLE) ............................................................................................................................... 15 3.9 TABLEBUFFERUSAGE ...................................................................................................................................... 15
3.10 RE-INDEXING.................................................................................................................................................. 17 3.11 REBUILD THEINDEX ......................................................................................................................................... 18 3.12 SCRIPTS FORDEFRAGMENTATION OF THEINDEX .................................................................................................... 19 3.13 SCRIPTS FOR GENERATINGDROP & CREATEINDEX ................................................................................................. 21 3.14 TO GET THE AGGREGATE PERFORMANCE STATISTICS FROMPLANCACHE..................................................................... 24
8/10/2019 Best SQL Practices on Performance
4/25
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 thenfetch 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 Ordersb. 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 Summary2. List View3. Total Count
b. In this case, Job Summary & Total Count output s will not vary unless we modify themain input filter conditions.
c. For sorting any columns in the list view, we should not load the Job Summary & TotalCount 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 dynamicallya. 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 nullbegin
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 conditiona. E.g. where isnull(order_date,) between @pi_start and @pi_end b. where cast(order_date as date) between @pi_start and @pi_endc. where dbo.fn_Xdate(order_date) between @pi_start and @pi_end
8/10/2019 Best SQL Practices on Performance
5/25
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 readfrom 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 largeDonts:
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.Dont s:
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_idleft 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 wo1Join 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_Idleft join ref_organization ro (nolock) on ro.id_org = wo1.vendor_idleft join ref_priority rp (nolock) on rp.priority_id= wo1.priority_id
10. Use Nolock for well known masters for fetching stored procedures.Dont s:
8/10/2019 Best SQL Practices on Performance
6/25
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 useindex defragment.
Eg:USE FPREO;
GODBCC 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 areperformed by the respective index. It the read process does not happen for a long period, thenwe do not need that index.
14. Try to have views for addresses, since we need to map many tables to fetch the address invarious 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 toavoid 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]',
8/10/2019 Best SQL Practices on Performance
7/25
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 Utilizationwill 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 logicalReads.
18. If a condition contains OR , then put that condition firsta. 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 SCHEMABINDINGBEGIN
RETURN @INPUT * 2 + 50ENDGO
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 (useComputed Columns /persisted Columns )
http://www.mssqltips.com/sqlservertip/1692/using-schema-binding-to-improve-sql-server-udf-http://www.mssqltips.com/sqlservertip/1692/using-schema-binding-to-improve-sql-server-udf-8/10/2019 Best SQL Practices on Performance
8/25
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 ) persistedgo
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(*) Usingcount(*) 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_idfor xml path ( '' ) ) as category
from dbo . wo_job wjwhere 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_idand wo1 . reviewer_id = @pi_login_id
)
Dos: and exists( select 1 from work_order wo1 ( nolock )
where wo . work_order_id = wo1 . work_order_idand 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:
8/10/2019 Best SQL Practices on Performance
9/25
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 EXISTSwhich will improve the performance drastically.
Donts: join ref_property re (nolock)join ref_property re1 (nolock) on re1.property_id= re.property_idand 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_idand exists ( select 1 ref_property re1 ( nolock )
where re1 . property_id = re . property_idand re1 . property_stage_id in ( 1 , 2 )
)
29. Avoid using global temporary ##temp table s in a stored procedures. It will not support whenconcurrent users perform simultaneously. i.e., same value will be passed to all users which leadsto 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 tableand then insert the records.
Donts: Select id,designation
into #tempfrom ref_designationwhere is_active = 1
Dos: Create table #temp (id bigint, designation nvarchar(400))
8/10/2019 Best SQL Practices on Performance
10/25
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 anew column is added in the specified table.
Donts: Insert into #tempSelect work_order_id, work_order_status_work_item_typefrom 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_typefrom 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 columnlist 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_activefrom 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 levelas SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED in the top of stored procedures.
8/10/2019 Best SQL Practices on Performance
11/25
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 storedprocedure. It would be applicable for the connections (Session ID) even though the respective Storedprocedures contains the nested Stored procedures/Remote Procedure. This should applicable for onlyfetch 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
http://msdn.microsoft.com/en-us/library/ms177500.aspxhttp://msdn.microsoft.com/en-us/library/ms177500.aspxhttp://msdn.microsoft.com/en-us/library/ms177500.aspxhttp://msdn.microsoft.com/en-us/library/ms177500.aspx8/10/2019 Best SQL Practices on Performance
12/25
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'
8/10/2019 Best SQL Practices on Performance
13/25
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 pjoin master . dbo . sysdatabases d on p . dbID = d . dbIDwhere 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
8/10/2019 Best SQL Practices on Performance
14/25
Performance Tuning & Maintenance Document Page 14 of 25
3.7 To Identification of unused IndexScan: 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 specificvalue 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 spWHERE 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 INTSELECT @dbid = DB_ID ( DB_NAME())SELECT OBJECTNAME = OBJECT_NAME( I . OBJECT_ID ),INDEXNAME = I . NAME,I . INDEX_IDFROM SYS. INDEXES IJOIN SYS. OBJECTS OON I . OBJECT_ID = O . OBJECT_IDWHERE OBJECTPROPERTY( O. OBJECT_ID , 'IsUserTable' ) = 1AND I . INDEX_ID NOT IN (SELECT S . INDEX_ID
FROM SYS. DM_DB_INDEX_USAGE_STATS SWHERE S . OBJECT_ID = I . OBJECT_IDAND I . INDEX_ID = S . INDEX_IDAND DATABASE_ID = @dbid )ORDER BY OBJECTNAME,I . INDEX_ID ,INDEXNAME ASCGO
8/10/2019 Best SQL Practices on Performance
15/25
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.CustomerIDAND c.TerritoryID = t.TerritoryIDAND 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] ,
http://www.mssqltips.com/sqlservertip/1354/optimize-parameter-driven-queries-with-sql-server-optimize-for-hint/http://www.mssqltips.com/sqlservertip/1354/optimize-parameter-driven-queries-with-sql-server-optimize-for-hint/http://www.mssqltips.com/sqlservertip/1354/optimize-parameter-driven-queries-with-sql-server-optimize-for-hint/http://www.mssqltips.com/sqlservertip/1354/optimize-parameter-driven-queries-with-sql-server-optimize-for-hint/8/10/2019 Best SQL Practices on Performance
16/25
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
8/10/2019 Best SQL Practices on Performance
17/25
Performance Tuning & Maintenance Document Page 17 of 25
3.11 Re-Indexing
CREATE PROCEDURE upd_reindexASbeginset 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 ),
nameFROM sys . tableswhere type = 'U'
if exists ( select 1 from #temp_reindex )
8/10/2019 Best SQL Practices on Performance
18/25
Performance Tuning & Maintenance Document Page 18 of 25
begin select @l_incr = 1
select @l_count = COUNT(*) from #temp_reindex
while @l_incr < = @l_countbegin
select @l_table_name = table_name
from #temp_reindex tpwhere sl_no = @l_incr
PRINT 'Reindexing Table: ' + @l_table_nameDBCC DBREINDEX ( @l_table_name , '' , 80 )
select @l_incr = @l_incr + 1
select @l_table_name = null
endend
EXEC sp_updatestats
set nocount offendgo
3.12 Rebuild the Index
create procedure rebuild_indexas 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 @DatabaseWHILE @@FETCH_STATUS = 0BEGIN
8/10/2019 Best SQL Practices on Performance
19/25
8/10/2019 Best SQL Practices on Performance
20/25
Performance Tuning & Maintenance Document Page 20 of 25
-- Declare a cursor.DECLARE tables CURSOR FOR
SELECT TABLE_SCHEMA + '.' + TABLE_NAMEFROM INFORMATION_SCHEMA. TABLESWHERE 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 tablesINTO @tablename ;
WHILE @@FETCH_STATUS = 0BEGIN-- Do the showcontig of all indexes of the table
INSERT INTO #fraglistEXEC ( '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
8/10/2019 Best SQL Practices on Performance
21/25
Performance Tuning & Maintenance Document Page 21 of 25
FROM #fraglistWHERE LogicalFrag >= @maxfrag
AND INDEXPROPERTY ( ObjectId , IndexName , 'IndexDepth' ) > 0 ;
-- Open the cursor.OPEN indexes ;
-- Loop through the indexes.FETCH NEXT
FROM indexesINTO @tablename , @objectid , @indexid , @frag ;
WHILE @@FETCH_STATUS = 0BEGIN
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 indexesINTO @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
SELECTixz . 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_definitionEND Filter_Definition
8/10/2019 Best SQL Practices on Performance
22/25
Performance Tuning & Maintenance Document Page 22 of 25
--For 2005, which did not have filtered indexes, comment out the aboveCASE statement, and uncomment this:INTO #tmp_indexesFROM sys . indexes ixzINNER JOIN sys . objects obzON ixz . object_id = obz . object_id INNER JOIN sys . schemas scmzON obz . schema_id = scmz . schema_id WHERE ixz . index_id > 0AND ixz . index_id < 255 ---- 0 = HEAP index, 255 = TEXT columns indexAND INDEXPROPERTY ( ixz . object_id , ixz . name , 'ISUNIQUE' ) = 0 -- comment outto include unique andAND INDEXPROPERTY ( ixz . object_id , ixz . name , 'ISCLUSTERED' ) = 0 -- commentout 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 FORSELECT tableid , indexidFROM #tmp_indexes
OPEN index_cursorFETCH NEXT FROM index_cursor INTO @tableid , @indexid
WHILE @@FETCH_STATUS - 1BEGINSELECT @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_columnWHEN 0THEN CASE ixcolz . is_descending_key
WHEN 1THEN @isql_key + COALESCE( colz . name , '' ) + ' DESC, 'ELSE @isql_key + COALESCE( colz . name , '' ) + ' ASC, 'ENDELSE @isql_keyEND,
--include column@isql_incl = CASE ixcolz . is_included_columnWHEN 1
8/10/2019 Best SQL Practices on Performance
23/25
8/10/2019 Best SQL Practices on Performance
24/25
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 + ']' ENDFROM #tmp_indexes tiJOIN sys . indexes i ON ti . Object_id = i . object_id and ti . indexname = i . nameJOIN sys . filegroups fg on i . data_space_id = fg . data_space_idWHERE LEFT( ti . tablename , 3 ) NOT IN ( 'sys' , 'dt_' ) --exclude system tablesORDER BY ti . tablename , ti . indexid , ti . indexname
--makes the dropSELECT 'DROP INDEX ' + ' ' + ( tablename ) + '.'+ ( indexname ) + ''FROM #tmp_indexesWHERE LEFT( tablename , 4 ) NOT IN ( '[sys' , 'dt_' )
----Drop the temp table againDROP 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 . TEXTFROM sys . dm_exec_query_stats
8/10/2019 Best SQL Practices on Performance
25/25
f f
CROSS APPLY sys . dm_exec_sql_text ( plan_handle ) WHERE total_logical_reads 0AND total_logical_writes 0ORDER BY ( total_logical_reads + total_logical_writes ) DESC