
2019-01-11
3,533 reads

USE Master
GO
SET NOCOUNT ON
GO
/**
* Author: Rodrigo Acosta
* e-mail: acosta_rodrigo@hotmail.com
* Compares performance of queries between different SQL Server instances.
* Execute at source, and will generate an output with TSQL commands to create temp tables filled with top 50 long lasting queries from all databases.
* Then execute output at destination and will do the same and show comparison of matching queries!!
*/IF OBJECT_ID('tempdb..#temp') IS NOT NULL DROP TABLE #temp
GO
-- Temp table that will store source server stats.
CREATE TABLE #temp(
[dbname] sysname,
query_hash varbinary(MAX),
execution_count bigint,
avg_time_milliseconds bigint,
max_cpu_time_milliseconds bigint,
max_physical_reads bigint,
max_logical_writes bigint)
-- Gather stats from all databases
DECLARE @fillQuery nvarchar(MAX);
SELECT @fillQuery = N'SELECT TOP 50 "?" as dbname,
HASHBYTES(' + '''' + 'SHA1' + '''' + ',CONVERT(varchar(8000), st.[text])) as text_checksum,
qs.execution_count,
((qs.total_elapsed_time / qs.execution_count) / 1000) AS avg_time_milliseconds,
max_worker_time / 1000 as max_cpu_time_milliseconds,
max_physical_reads,
max_logical_writes
FROM sys .dm_exec_query_stats AS qs CROSS APPLY sys.dm_exec_sql_text( qs.sql_handle ) AS st
ORDER BY qs.last_elapsed_time DESC;'
INSERT INTO #temp
exec sp_msforeachdb @fillQuery;
-- remove system databases stats
DELETE FROM #temp WHERE dbname IN (SELECT name FROM sysdatabases WHERE dbid < 5)
-- variables declaration
DECLARE @dbname sysname;
DECLARE @query_hash varbinary(MAX);
DECLARE @cmd varchar(MAX), @insert varchar(MAX);
DECLARE @execution_count bigint,
@avg_time_milliseconds bigint,
@max_cpu_time_milliseconds bigint,
@max_physical_reads bigint,
@max_logical_writes bigint
--define initial command
SELECT @cmd = 'CREATE TABLE #tempSource ([dbname] sysname, query_hash nvarchar(4000), execution_count bigint, avg_time_milliseconds bigint, max_cpu_time_milliseconds bigint, max_physical_reads bigint, max_logical_writes bigint) '
-- creates output table
IF OBJECT_ID('tempdb..#output') IS NOT NULL DROP TABLE #output
CREATE TABLE #output (id int IDENTITY(1,1), output nvarchar(MAX))
-- insert initial commands.
INSERT INTO #output (output) VALUES ('SET NOCOUNT ON');
INSERT INTO #output (output) VALUES ('GO');
INSERT INTO #output (output) VALUES (@cmd);
-- cursor that will iterate the temp table to form the final query
DECLARE statsCursor CURSOR LOCAL FORWARD_ONLY
FOR SELECT dbname, query_hash, MAX(execution_count), MAX(avg_time_milliseconds), MAX(max_cpu_time_milliseconds), MAX(max_physical_reads), MAX(max_logical_writes) FROM #temp GROUP BY dbname, query_hash ORDER BY dbname, query_hash
OPEN statsCursor
FETCH NEXT FROM statsCursor INTO @dbname, @query_hash, @execution_count, @avg_time_milliseconds, @max_cpu_time_milliseconds, @max_physical_reads, @max_logical_writes
WHILE (@@FETCH_STATUS = 0)
BEGIN
SELECT @insert = 'INSERT INTO #tempSource (dbname, query_hash, execution_count, avg_time_milliseconds, max_cpu_time_milliseconds, max_physical_reads, max_logical_writes) VALUES (' + '''' + @dbname + '''' + ', ' + '''' + CONVERT(nvarchar(4000), master.dbo.fn_varbintohexstr(@query_hash)) + '''' + ', ' + CONVERT(nvarchar(10), @execution_count) + ', ' + CONVERT(nvarchar(10), @avg_time_milliseconds) + ', ' + CONVERT(nvarchar(10), @max_cpu_time_milliseconds) + ', ' + CONVERT(nvarchar(10), @max_physical_reads) + ', ' + CONVERT(nvarchar(10), @max_logical_writes) + ')'
INSERT INTO #output (output) VALUES (@insert);
FETCH NEXT FROM statsCursor INTO @dbname, @query_hash, @execution_count, @avg_time_milliseconds, @max_cpu_time_milliseconds, @max_physical_reads, @max_logical_writes
END
CLOSE statsCursor;
DEALLOCATE statsCursor;
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Prepare statements to be executed at the destination server.
DECLARE @cmd2 nvarchar(MAX)
SELECT @cmd2 = N'CREATE TABLE #tempDestination(
[dbname] sysname,
[text] ntext,
query_hash varbinary(4000),
execution_count bigint,
avg_time_milliseconds bigint,
max_cpu_time_milliseconds bigint,
max_physical_reads bigint,
max_logical_writes bigint)'
INSERT INTO #output (output) VALUES (@cmd2)
SELECT @fillQuery = REPLACE(@fillQuery,'as dbname,', 'as dbname, [text], '); -- I'm adding the Text column to the table in destination
SELECT @cmd2 = 'DECLARE @cmd nvarchar(MAX); SELECT @cmd = ' + '''' + REPLACE(@fillQuery, '''', '''''') + '''' + ' INSERT INTO #tempDestination exec sp_msforeachdb @cmd'
INSERT INTO #output (output) VALUES (@cmd2)
SELECT @cmd2 = N'
SELECT a.dbname db_source, b.dbname db_destination, b.[text], a.query_hash, a.execution_count exec_count_source, b.execution_count exec_count_destination, a.avg_time_milliseconds avg_time_millis_source, b.avg_time_milliseconds avg_time_millis_destination,
a.max_cpu_time_milliseconds max_cpu_time_millis_source, b.max_cpu_time_milliseconds max_cpu_time_millis_destination, a.max_physical_reads max_physical_reads_source, b.max_physical_reads max_physical_reads_destination, a.max_logical_writes max_log_writes_source, b.max_logical_writes max_log_writes_destination
FROM #tempSource a
INNER JOIN #tempDestination b
ON a.query_hash = CONVERT(nvarchar(4000), master.dbo.fn_varbintohexstr(b.query_hash))
ORDER BY b.dbname'
INSERT INTO #output (output) VALUES (@cmd2)
SELECT @cmd2 = N'-- SELECT * FROM #tempSource;'
INSERT INTO #output (output) VALUES (@cmd2)
SELECT @cmd2 = N'-- SELECT * FROM #tempDestination;'
INSERT INTO #output (output) VALUES (@cmd2)
SELECT output FROM #output ORDER BY id