This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
----------------------------------------------------------------------- | |
-- Collection of queries that helps in performance tuning | |
--grant permission to user | |
GRANT VIEW SERVER STATE TO [user_name] | |
------------ based on http://msdn.microsoft.com/en-us/magazine/cc135978.aspx | |
---------- Uncover hidden data to optimize application performance ----------------------- | |
-- * Number of missing indexes | |
SELECT | |
DatabaseName = DB_NAME(database_id) | |
,[Number Indexes Missing] = count(*) | |
FROM sys.dm_db_missing_index_details | |
GROUP BY DB_NAME(database_id) | |
ORDER BY 2 DESC; | |
-- * top costly missing indexes | |
SELECT TOP 10 | |
[Total Cost] = ROUND(avg_total_user_cost * avg_user_impact * (user_seeks + user_scans),0) | |
, avg_user_impact | |
, TableName = statement | |
, [EqualityUsage] = equality_columns | |
, [InequalityUsage] = inequality_columns | |
, [Include Cloumns] = included_columns | |
, s.unique_compiles | |
,s.last_user_seek | |
,s.user_seeks | |
FROM sys.dm_db_missing_index_groups g | |
INNER JOIN sys.dm_db_missing_index_group_stats s | |
ON s.group_handle = g.index_group_handle | |
INNER JOIN sys.dm_db_missing_index_details d | |
ON d.index_handle = g.index_handle | |
ORDER BY [Total Cost] DESC; | |
-- * top costly unused indexes (does not seem to work) | |
-- Create required table structure only. | |
-- Note: this SQL must be the same as in the Database loop given in the following step. | |
SELECT TOP 1 | |
DatabaseName = DB_NAME() | |
,TableName = OBJECT_NAME(s.[object_id]) | |
,IndexName = i.name | |
,user_updates | |
,system_updates | |
-- Useful fields below: | |
--, * | |
INTO #TempUnusedIndexes | |
FROM sys.dm_db_index_usage_stats s | |
INNER JOIN sys.indexes i ON s.[object_id] = i.[object_id] | |
AND s.index_id = i.index_id | |
WHERE s.database_id = DB_ID() | |
AND OBJECTPROPERTY(s.[object_id], 'IsMsShipped') = 0 | |
AND user_seeks = 0 | |
AND user_scans = 0 | |
AND user_lookups = 0 | |
AND s.[object_id] = -999 -- Dummy value to get table structure. | |
; | |
-- Loop around all the databases on the server. | |
EXEC sp_MSForEachDB 'USE [?]; | |
-- Table already exists. | |
INSERT INTO #TempUnusedIndexes | |
SELECT TOP 10 | |
DatabaseName = DB_NAME() | |
,TableName = OBJECT_NAME(s.[object_id]) | |
,IndexName = i.name | |
,user_updates | |
,system_updates | |
FROM sys.dm_db_index_usage_stats s | |
INNER JOIN sys.indexes i ON s.[object_id] = i.[object_id] | |
AND s.index_id = i.index_id | |
WHERE s.database_id = DB_ID() | |
AND OBJECTPROPERTY(s.[object_id], ''IsMsShipped'') = 0 | |
AND user_seeks = 0 | |
AND user_scans = 0 | |
AND user_lookups = 0 | |
AND i.name IS NOT NULL -- Ignore HEAP indexes. | |
ORDER BY user_updates DESC | |
; | |
' | |
-- Select records. | |
SELECT TOP 10 * FROM #TempUnusedIndexes ORDER BY [user_updates] DESC | |
-- Tidy up. | |
DROP TABLE #TempUnusedIndexes | |
-- * most costly used indexes | |
-- Create required table structure only. | |
-- Note: this SQL must be the same as in the Database loop given in the following step. | |
SELECT TOP 1 | |
[Maintenance cost] = (user_updates + system_updates) | |
,[Retrieval usage] = (user_seeks + user_scans + user_lookups) | |
,DatabaseName = DB_NAME() | |
,TableName = OBJECT_NAME(s.[object_id]) | |
,IndexName = i.name | |
INTO #TempMaintenanceCost | |
FROM sys.dm_db_index_usage_stats s | |
INNER JOIN sys.indexes i ON s.[object_id] = i.[object_id] | |
AND s.index_id = i.index_id | |
WHERE s.database_id = DB_ID() | |
AND OBJECTPROPERTY(s.[object_id], 'IsMsShipped') = 0 | |
AND (user_updates + system_updates) > 0 -- Only report on active rows. | |
AND s.[object_id] = -999 -- Dummy value to get table structure. | |
; | |
-- Loop around all the databases on the server. | |
EXEC sp_MSForEachDB 'USE [?]; | |
-- Table already exists. | |
INSERT INTO #TempMaintenanceCost | |
SELECT TOP 10 | |
[Maintenance cost] = (user_updates + system_updates) | |
,[Retrieval usage] = (user_seeks + user_scans + user_lookups) | |
,DatabaseName = DB_NAME() | |
,TableName = OBJECT_NAME(s.[object_id]) | |
,IndexName = i.name | |
FROM sys.dm_db_index_usage_stats s | |
INNER JOIN sys.indexes i ON s.[object_id] = i.[object_id] | |
AND s.index_id = i.index_id | |
WHERE s.database_id = DB_ID() | |
AND i.name IS NOT NULL -- Ignore HEAP indexes. | |
AND OBJECTPROPERTY(s.[object_id], ''IsMsShipped'') = 0 | |
AND (user_updates + system_updates) > 0 -- Only report on active rows. | |
ORDER BY [Maintenance cost] DESC | |
; | |
' | |
-- Select records. | |
SELECT TOP 10 * FROM #TempMaintenanceCost | |
ORDER BY [Maintenance cost] DESC | |
-- Tidy up. | |
DROP TABLE #TempMaintenanceCost | |
-- * most fragmented indexes | |
-- Create required table structure only. | |
-- Note: this SQL must be the same as in the Database loop given in the -- following step. | |
SELECT TOP 1 | |
DatbaseName = DB_NAME() | |
,TableName = OBJECT_NAME(s.[object_id]) | |
,IndexName = i.name | |
,[Fragmentation %] = ROUND(avg_fragmentation_in_percent,2) | |
INTO #TempFragmentation | |
FROM sys.dm_db_index_physical_stats(db_id(),null, null, null, null) s | |
INNER JOIN sys.indexes i ON s.[object_id] = i.[object_id] | |
AND s.index_id = i.index_id | |
WHERE s.[object_id] = -999 -- Dummy value just to get table structure. | |
; | |
-- Loop around all the databases on the server. | |
EXEC sp_MSForEachDB 'USE [?]; | |
-- Table already exists. | |
INSERT INTO #TempFragmentation | |
SELECT TOP 10 | |
DatbaseName = DB_NAME() | |
,TableName = OBJECT_NAME(s.[object_id]) | |
,IndexName = i.name | |
,[Fragmentation %] = ROUND(avg_fragmentation_in_percent,2) | |
FROM sys.dm_db_index_physical_stats(db_id(),null, null, null, null) s | |
INNER JOIN sys.indexes i ON s.[object_id] = i.[object_id] | |
AND s.index_id = i.index_id | |
WHERE s.database_id = DB_ID() | |
AND i.name IS NOT NULL -- Ignore HEAP indexes. | |
AND OBJECTPROPERTY(s.[object_id], ''IsMsShipped'') = 0 | |
ORDER BY [Fragmentation %] DESC | |
; | |
' | |
-- Select records. | |
SELECT TOP 10 * FROM #TempFragmentation ORDER BY [Fragmentation %] DESC | |
-- Tidy up. | |
DROP TABLE #TempFragmentation | |
-- * Find top server waits (CLR_AUTO_EVENT, CHECKPOINT_QUEUE, PAGEIOLATCH_SH, ...) | |
SELECT TOP 10 | |
[Wait type] = wait_type, | |
[Wait time (s)] = wait_time_ms / 1000, | |
[% waiting] = CONVERT(DECIMAL(12,2), wait_time_ms * 100.0 | |
/ SUM(wait_time_ms) OVER()) | |
FROM sys.dm_os_wait_stats | |
WHERE wait_type NOT LIKE '%SLEEP%' | |
ORDER BY wait_time_ms DESC; | |
-- * Find top queries by average time blocked | |
SELECT TOP 100 | |
[Average Time Blocked (s)] = (total_elapsed_time - total_worker_time) / (qs.execution_count * 1000000) | |
,[Total Time Blocked (s)] = total_elapsed_time - total_worker_time / 1000000 | |
,[Execution count] = qs.execution_count | |
-- quote text into CSV friendly format | |
,[Individual Query] = '"' + SUBSTRING (qt.text,qs.statement_start_offset/2 + 1, | |
(CASE WHEN qs.statement_end_offset = -1 | |
THEN LEN(CONVERT(NVARCHAR(MAX), qt.text)) * 2 | |
ELSE qs.statement_end_offset END - qs.statement_start_offset)/2) | |
+ '"' | |
-- quote text into CSV friendly format | |
,[Parent Query] = '"' + qt.text + '"' | |
,DatabaseName = DB_NAME(qt.dbid) | |
FROM sys.dm_exec_query_stats qs | |
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) as qt | |
ORDER BY [Average Time Blocked (s)] DESC; | |
-- * Find most executed queries by number of executions (repetitions) | |
SELECT TOP 10 | |
[Execution count] = execution_count | |
,[Individual Query] = SUBSTRING (qt.text,qs.statement_start_offset/2 + 1, | |
(CASE WHEN qs.statement_end_offset = -1 | |
THEN LEN(CONVERT(NVARCHAR(MAX), qt.text)) * 2 | |
ELSE qs.statement_end_offset END - qs.statement_start_offset)/2) | |
,[Parent Query] = qt.text | |
,DatabaseName = DB_NAME(qt.dbid) | |
FROM sys.dm_exec_query_stats qs | |
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) as qt | |
ORDER BY [Execution count] DESC; | |
-- * Find top queries by average CPU | |
SELECT TOP 100 | |
[Average CPU (s)] = total_worker_time / (qs.execution_count * 1000000) --Unit: sec | |
,[Total CPU (s)] = total_worker_time / 1000000 | |
,[Execution count] = qs.execution_count | |
,qs.last_execution_time | |
-- quote text into CSV friendly format | |
,[Individual Query] = '"' + SUBSTRING (qt.text,qs.statement_start_offset/2 + 1, | |
(CASE WHEN qs.statement_end_offset = -1 | |
THEN LEN(CONVERT(NVARCHAR(MAX), qt.text)) * 2 | |
ELSE qs.statement_end_offset END - | |
qs.statement_start_offset)/2) + '"' | |
-- quote text into CSV friendly format | |
,[Parent Query] = '"' + qt.text + '"' | |
,DatabaseName = DB_NAME(qt.dbid) | |
FROM sys.dm_exec_query_stats qs | |
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) as qt | |
where last_execution_time > '2012-03-10 7:00AM' | |
ORDER BY [Average CPU (s)] DESC; | |
-- * Find top queries by total CPU | |
SELECT TOP 100 | |
[Average CPU (s)] = total_worker_time / (qs.execution_count * 1000000) | |
,[Total CPU (s)] = total_worker_time / 1000000 | |
,[Execution count] = qs.execution_count | |
-- quote text into CSV friendly format | |
,[Individual Query] = '"' + SUBSTRING (qt.text,qs.statement_start_offset/2 + 1, | |
(CASE WHEN qs.statement_end_offset = -1 | |
THEN LEN(CONVERT(NVARCHAR(MAX), qt.text)) * 2 | |
ELSE qs.statement_end_offset END - | |
qs.statement_start_offset)/2) + '"' | |
-- quote text into CSV friendly format | |
,[Parent Query] = '"' + qt.text + '"' | |
,DatabaseName = DB_NAME(qt.dbid) | |
FROM sys.dm_exec_query_stats qs | |
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) as qt | |
ORDER BY [Total CPU (s)] DESC; | |
-- * list of all current traces | |
select * from sys.traces | |
-- * list all categories, classes and columns available to SQL trace | |
/* | |
Script: CH10 Trace View.sql | |
Author: James Rowland-Jones | |
Title: Professional SQL Server 2008: Internals and Performance Tuning | |
*/ | |
SELECT cat.name AS category_name | |
,ev.name AS event_name | |
,col.name AS column_name | |
,col.is_filterable AS column_is_filterable | |
,col.max_size AS column_max_size | |
,col.[type_name] AS column_type_name | |
,sub.subclass_name AS subclass_name | |
,sub.subclass_value AS subclass_value | |
FROM sys.trace_categories cat | |
JOIN sys.trace_events ev | |
ON cat.category_id = ev.category_id | |
JOIN sys.trace_event_bindings bi | |
ON bi.trace_event_id = ev.trace_event_id | |
JOIN sys.trace_columns col | |
ON bi.trace_column_id = col.trace_column_id | |
LEFT JOIN sys.trace_subclass_values sub | |
ON sub.trace_column_id = bi.trace_column_id | |
AND sub.trace_event_id = bi.trace_event_id | |
ORDER BY cat.name | |
,ev.name | |
,col.name | |
,sub.subclass_value | |
-- * List definition of defatul SQL trace | |
/* | |
Script: CH10 Trace Definition.sql | |
Author: James Rowland-Jones | |
Title: Professional SQL Server 2008: Internals and Performance Tuning | |
*/ | |
DECLARE @vTraceID INT; | |
SET @vTraceID = 1; | |
WITH filter AS | |
( SELECT columnid | |
,CASE logical_operator | |
WHEN 0 THEN 'AND' | |
WHEN 1 THEN 'OR' | |
ELSE 'err' | |
END AS logical_operator | |
,CASE comparison_operator | |
WHEN 0 THEN ' = ' | |
WHEN 1 THEN ' <> ' | |
WHEN 2 THEN ' > ' | |
WHEN 3 THEN ' < ' | |
WHEN 4 THEN ' >= ' | |
WHEN 5 THEN ' <= ' | |
WHEN 6 THEN ' LIKE ' | |
WHEN 7 THEN ' NOT LIKE ' | |
END AS comparison_operator | |
,value | |
FROM ::fn_trace_getfilterinfo(@vTraceID) | |
) | |
SELECT cat.name AS CategoryName | |
,evt.name AS EventName | |
,col.name AS ColumnName | |
,STUFF ( ( SELECT ' | ' + child_fil.logical_operator | |
+ child_fil.comparison_operator | |
+ CAST(child_fil.value AS VARCHAR(MAX) | |
) | |
FROM filter child_fil | |
WHERE parent_fil.columnid = child_fil.columnid | |
FOR XML PATH ('') | |
),1,1,'' | |
) AS ColFilters | |
FROM fn_trace_geteventinfo(@vTraceID) evi | |
JOIN sys.trace_events evt | |
ON evi.eventid = evt.trace_event_id | |
JOIN sys.trace_categories cat | |
ON evt.category_id = cat.category_id | |
JOIN sys.trace_columns col | |
ON evi.columnid = col.trace_column_id | |
LEFT JOIN filter parent_fil | |
ON col.trace_column_id = parent_fil.columnid | |
GROUP BY cat.name | |
,evt.name | |
,col.name | |
,parent_fil.columnid | |
ORDER BY cat.name | |
,evt.name | |
,col.name | |