diff --git a/SQL Performance Dashboard.pbit b/SQL Performance Dashboard.pbit index 1e205aaa..669be4a6 100644 Binary files a/SQL Performance Dashboard.pbit and b/SQL Performance Dashboard.pbit differ diff --git a/SQLWATCHDB/Properties/AssemblyInfo.cs b/SQLWATCHDB/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..fe24ea46 --- /dev/null +++ b/SQLWATCHDB/Properties/AssemblyInfo.cs @@ -0,0 +1,23 @@ +using System.Reflection; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with the SQLCLR assembly. +[assembly: AssemblyTitle("SQLWATCH")] +[assembly: AssemblyDescription("https://sqlwatch.io")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("SQLWATCH")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/SQLWATCHDB/SQLWATCH.refactorlog b/SQLWATCHDB/SQLWATCH.refactorlog new file mode 100644 index 00000000..82443593 --- /dev/null +++ b/SQLWATCHDB/SQLWATCH.refactorlog @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SQLWATCHDB/SQLWATCH.sqlproj b/SQLWATCHDB/SQLWATCH.sqlproj index 2abf2b7a..29f081bf 100644 --- a/SQLWATCHDB/SQLWATCH.sqlproj +++ b/SQLWATCHDB/SQLWATCH.sqlproj @@ -52,6 +52,7 @@ true prompt 4 + False 11.0 @@ -90,7 +91,10 @@ - + + + + @@ -107,4 +111,24 @@ msdb + + + + + + + + + + + + + + + + $(IntermediateTargetAssembly.Split(',')[1].Split('=')[1]) + + + + \ No newline at end of file diff --git a/SQLWATCHDB/SQLWATCH.sqlproj.user b/SQLWATCHDB/SQLWATCH.sqlproj.user index d014f64c..530a7233 100644 --- a/SQLWATCHDB/SQLWATCH.sqlproj.user +++ b/SQLWATCHDB/SQLWATCH.sqlproj.user @@ -1,3 +1,7 @@  + + (Blank) + StartNone + \ No newline at end of file diff --git a/SQLWATCHDB/Script.PostDeployment1.sql b/SQLWATCHDB/Script.PostDeployment1.sql index 1c827f24..efd9c8f2 100644 --- a/SQLWATCHDB/Script.PostDeployment1.sql +++ b/SQLWATCHDB/Script.PostDeployment1.sql @@ -19,6 +19,17 @@ if (select count(*) from [dbo].[sql_perf_mon_config_who_is_active_age]) = 0 -------------------------------------------------------------------------------------- -- -------------------------------------------------------------------------------------- +/* databases with create_date = '1970-01-01' are from previous +versions of SQLWATCH and we will now update create_date to the actual +create_date (this will only apply to upgrades) */ +update swd + set [database_create_date] = db.[create_date] +from [dbo].[sql_perf_mon_database] swd +inner join sys.databases db + on db.[name] = swd.[database_name] + and swd.[database_create_date] = '1970-01-01' + +/* now add new databases */ exec [dbo].[sp_sql_perf_mon_add_database] -------------------------------------------------------------------------------------- @@ -50,7 +61,7 @@ create nonclustered index tmp_idx_sql_perf_mon_perf_counters_types on #sql_perf_ /* based on https://blogs.msdn.microsoft.com/dfurman/2015/04/02/collecting-performance-counter-values-from-a-sql-azure-database/ */ insert into #sql_perf_mon_config_perf_counters([collect],[object_name],[counter_name], [instance_name],[base_counter_name]) values - (0,'Access Methods','Forwarded Records/sec','',NULL) + (0,'Access Methods','Forwarded Records/sec','',NULL) ,(1,'Access Methods','Full Scans/sec','',NULL) ,(1,'Access Methods','Page Splits/sec','',NULL) ,(1,'Access Methods','Pages Allocated/sec','',NULL) @@ -619,14 +630,23 @@ if (select count(*) from [dbo].[sql_perf_mon_config_report_time_interval]) = 0 end -------------------------------------------------------------------------------------- --- +-- add snapshot types -------------------------------------------------------------------------------------- -if (select count(*) from [dbo].[sql_perf_mon_config_snapshot_type]) = 0 - begin - insert into [dbo].[sql_perf_mon_config_snapshot_type] - values (1, 'Performance', 7), - (2, 'Growth', 365) - end +;merge [dbo].[sql_perf_mon_config_snapshot_type] as target +using ( + /* performance data logger */ + select [snapshot_type_id] = 1, [snapshot_type_desc] = 'Performance', [snapshot_retention_days] = 7 + union + /* size data logger */ + select [snapshot_type_id] = 2, [snapshot_type_desc] = 'Disk Utilisation', [snapshot_retention_days] = 365 +) as source +on (source.[snapshot_type_id] = target.[snapshot_type_id]) +when matched and source.[snapshot_type_desc] <> target.[snapshot_type_desc] then + update set [snapshot_type_desc] = source.[snapshot_type_desc] +when not matched then + insert ([snapshot_type_id],[snapshot_type_desc],[snapshot_retention_days]) + values (source.[snapshot_type_id],source.[snapshot_type_desc],source.[snapshot_retention_days]) +; -------------------------------------------------------------------------------------- -- @@ -791,3 +811,53 @@ if (select name from sysjobs where name = 'DBA-PERF-AUTO-CONFIG') is null @active_end_time=235959, @schedule_id = @schedule_id OUTPUT select @schedule_id end + +if (select name from sysjobs where name = 'SQLWATCH-LOGGER-DISK-UTILISATION') is null + begin + set @jobId = null + EXEC msdb.dbo.sp_add_job @job_name=N'SQLWATCH-LOGGER-DISK-UTILISATION', + @enabled=1, + @notify_level_eventlog=0, + @notify_level_email=2, + @notify_level_page=2, + @delete_level=0, + @category_name=N'Data Collector', + @owner_login_name=N'sa', @job_id = @jobId OUTPUT + EXEC msdb.dbo.sp_add_jobserver @job_name=N'SQLWATCH-LOGGER-DISK-UTILISATION', @server_name = @server; + EXEC msdb.dbo.sp_add_jobstep @job_name=N'SQLWATCH-LOGGER-DISK-UTILISATION', @step_name=N'exec dbo.usp_logger_disk_utilisation', + @step_id=1, + @cmdexec_success_code=0, + @on_success_action=1, + @on_fail_action=2, + @retry_attempts=0, + @retry_interval=0, + @os_run_priority=0, @subsystem=N'TSQL', + @command=N'exec [dbo].[usp_logger_disk_utilisation]', + @database_name=@database, + @flags=0 + EXEC msdb.dbo.sp_update_job @job_name=N'SQLWATCH-LOGGER-DISK-UTILISATION', + @enabled=1, + @start_step_id=1, + @notify_level_eventlog=0, + @notify_level_email=2, + @notify_level_page=2, + @delete_level=0, + @description=N'', + @category_name=N'Data Collector', + @owner_login_name=N'sa', + @notify_email_operator_name=N'', + @notify_page_operator_name=N'' + set @schedule_id = null + EXEC msdb.dbo.sp_add_jobschedule @job_name=N'SQLWATCH-LOGGER-DISK-UTILISATION', @name=N'SQLWATCH-LOGGER-DISK-UTILISATION', + @enabled=1, + @freq_type=4, + @freq_interval=1, + @freq_subday_type=8, + @freq_subday_interval=1, + @freq_relative_interval=0, + @freq_recurrence_factor=1, + @active_start_date=20180909, + @active_end_date=99991231, + @active_start_time=437, + @active_end_time=235959, @schedule_id = @schedule_id OUTPUT + end \ No newline at end of file diff --git a/SQLWATCHDB/bin/Debug/SQLWATCH.dacpac b/SQLWATCHDB/bin/Debug/SQLWATCH.dacpac index 017e64ed..b681c316 100644 Binary files a/SQLWATCHDB/bin/Debug/SQLWATCH.dacpac and b/SQLWATCHDB/bin/Debug/SQLWATCH.dacpac differ diff --git a/SQLWATCHDB/bin/Debug/SQLWATCHDB.dll b/SQLWATCHDB/bin/Debug/SQLWATCHDB.dll index d3c0e280..b49d23c5 100644 Binary files a/SQLWATCHDB/bin/Debug/SQLWATCHDB.dll and b/SQLWATCHDB/bin/Debug/SQLWATCHDB.dll differ diff --git a/SQLWATCHDB/dbo/Procedures/sp_sql_perf_mon_add_database.sql b/SQLWATCHDB/dbo/Procedures/sp_sql_perf_mon_add_database.sql index bc426674..18f4a1d5 100644 --- a/SQLWATCHDB/dbo/Procedures/sp_sql_perf_mon_add_database.sql +++ b/SQLWATCHDB/dbo/Procedures/sp_sql_perf_mon_add_database.sql @@ -16,20 +16,36 @@ as set nocount on; declare @databases table ( - [database_name] sysname primary key - ) + [database_name] sysname not null, + [database_create_date] datetime not null default '1970-01-01', + primary key clustered ( + [database_name],[database_create_date] + ) +) insert into @databases - select [name] from sys.databases + select [name], [create_date] + from sys.databases union all /* mssqlsystemresource database appears in the performance counters so we need it as a dimensions to be able to filter in the report */ - select 'mssqlsystemresource' + select 'mssqlsystemresource', '1970-01-01' + + ;merge [dbo].[sql_perf_mon_database] as target + using @databases as source + on ( + source.[database_name] = target.[database_name] + and source.[database_create_date] = target.[database_create_date] + ) + /* dropped databases are going to be updated to current = 0 */ + when not matched by source then + update set [database_current] = 0 + /* new databases are going to be inserted */ + when not matched by target then + insert ([database_name], [database_create_date]) + values (source.[database_name], source.[database_create_date]); - insert into [dbo].[sql_perf_mon_database] - select s.[database_name] - from @databases s - left join [dbo].[sql_perf_mon_database] t - on s.[database_name] = t.[database_name] - where t.[database_name] is null - \ No newline at end of file + /* the above only accounts for databases that have been removed and re-added + if you rename database it will be treated as if it was removed and new + database created so you will lose history continuation. Why would you + rename a database anyway */ \ No newline at end of file diff --git a/SQLWATCHDB/dbo/Procedures/sp_sql_perf_mon_logger.sql b/SQLWATCHDB/dbo/Procedures/sp_sql_perf_mon_logger.sql index 00cb72b1..837c4e30 100644 --- a/SQLWATCHDB/dbo/Procedures/sp_sql_perf_mon_logger.sql +++ b/SQLWATCHDB/dbo/Procedures/sp_sql_perf_mon_logger.sql @@ -19,7 +19,7 @@ declare @sql nvarchar(4000) -------------------------------------------------------------------------------------------------------------- -- detect which version of sql we are running as some dmvs are different in different versions of sql -------------------------------------------------------------------------------------------------------------- - set @product_version = convert(nvarchar(128),serverproperty('productversioN')); + set @product_version = convert(nvarchar(128),serverproperty('productversion')); select @product_version_major = substring(@product_version, 1,charindex('.', @product_version) + 1 ) @@ -56,7 +56,6 @@ declare @sql nvarchar(4000) set @date_snapshot_current = getdate(); insert into [dbo].[sql_perf_mon_snapshot_header] values (@date_snapshot_current, 1) - -------------------------------------------------------------------------------------------------------------- -- 1. get cpu diff --git a/SQLWATCHDB/dbo/Procedures/usp_logger_disk_utilisation.sql b/SQLWATCHDB/dbo/Procedures/usp_logger_disk_utilisation.sql new file mode 100644 index 00000000..9b73ee9e --- /dev/null +++ b/SQLWATCHDB/dbo/Procedures/usp_logger_disk_utilisation.sql @@ -0,0 +1,111 @@ +CREATE PROCEDURE [dbo].[usp_logger_disk_utilisation] +AS +set nocount on; + +declare @snapshot_type tinyint = 2 +declare @product_version nvarchar(128) +declare @product_version_major decimal(10,2) +declare @product_version_minor decimal(10,2) + +set @product_version = convert(nvarchar(128),serverproperty('productversion')); +select @product_version_major = substring(@product_version, 1,charindex('.', @product_version) + 1 ) + ,@product_version_minor = parsename(convert(varchar(32), @product_version), 2); +-------------------------------------------------------------------------------------------------------------- +-- set the basics +-------------------------------------------------------------------------------------------------------------- +declare @snapshot_time datetime = getdate(); +insert into [dbo].[sql_perf_mon_snapshot_header] +values (@snapshot_time, @snapshot_type) + +-------------------------------------------------------------------------------------------------------------- +-- get sp_spaceused +-------------------------------------------------------------------------------------------------------------- +declare @spaceused table ( + [database_name] nvarchar(128), + [database_size] varchar(18), + [unallocated_space] varchar(18), + [reserved] varchar(18), + [data] varchar(18), + [index_size] varchar(18), + [unused] varchar(18) +) +insert into @spaceused + exec sp_MSforeachdb 'use [?]; exec sp_spaceused @oneresultset = 1;' + +-------------------------------------------------------------------------------------------------------------- +-- get log usage +-------------------------------------------------------------------------------------------------------------- +declare @logspace_SQL2008 table ( + [database_name] sysname, + [log_space_mb] decimal(18,2), + [log_space_used_perc] real, + [status] bit +) + +declare @logspace table ( + [database_name] sysname, + [total_log_size_in_bytes] bigint, + [used_log_space_in_bytes] bigint +) + +if @product_version_major < 11 + begin + insert into @logspace_SQL2008 + exec ('DBCC SQLPERF(LOGSPACE);') + + /* make into a 2012 format */ + insert into @logspace + select + [database_name], + [total_log_size_in_bytes] = [log_space_mb] * 1024.0 * 1024.0, + [used_log_space_in_bytes] = ([log_space_mb] * [log_space_used_perc] / 100.0) * 1024.0 * 1024.0 + from @logspace_SQL2008 + end +else + begin + insert into @logspace + exec sp_MSforeachdb ' + use [?] + select + ''?'', + [total_log_size_in_bytes], + [used_log_space_in_bytes] + from sys.dm_db_log_space_usage' + end + + +-------------------------------------------------------------------------------------------------------------- +-- combine results and insert into the table +-------------------------------------------------------------------------------------------------------------- +insert into [dbo].[logger_disk_utilisation_database] +select + su.[database_name] + , [database_create_date] = db.create_date + /* + conversion from sp_spaceused MiB format to bytes so we have consistent units + to test that this gives us an exact number: + sp_spaceused returns 7.63 MB for master database. + our conversion below gives us 8000634 bytes -> covnert back to MB: + 8000634 / 1024 / 1024 = 7.6299 MB + Try: http://www.wolframalpha.com/input/?i=8000634+bytes+in+MiB + */ + , [database_size_bytes] = convert(bigint,convert(decimal(19,2),replace([database_size],' MB','')) * 1024 * 1024) + , [unallocated_space_bytes] = convert(bigint,convert(decimal(19,2),replace([unallocated_space],' MB','')) * 1024.0 * 1024.0) + , [reserved_bytes] = convert(bigint,convert(decimal(19,2),replace([reserved],' KB','')) * 1024.0) + , [data_bytes] = convert(bigint,convert(decimal(19,2),replace([data],' KB','')) * 1024.0) + , [index_size_bytes] = convert(bigint,convert(decimal(19,2),replace([index_size],' KB','')) * 1024.0) + , [unused_bytes] = convert(bigint,convert(decimal(19,2),replace([unused],' KB','')) * 1024.0) + , ls.[total_log_size_in_bytes] + , ls.[used_log_space_in_bytes] + , [snapshot_time] = @snapshot_time + , [snapshot_type_id] = @snapshot_type +from @spaceused su +inner join @logspace ls + on su.[database_name] = ls.[database_name] +inner join sys.databases db + on db.[name] = su.[database_name] +/* join on sqlwatch database list otherwise it will fail + for newly created databases not yet added to the list */ +inner join [dbo].[sql_perf_mon_database] swd + on swd.[database_name] = db.[name] + and swd.[database_create_date] = db.[create_date] diff --git a/SQLWATCHDB/dbo/Tables/logger_disk_utilisation_database.sql b/SQLWATCHDB/dbo/Tables/logger_disk_utilisation_database.sql new file mode 100644 index 00000000..f58034f5 --- /dev/null +++ b/SQLWATCHDB/dbo/Tables/logger_disk_utilisation_database.sql @@ -0,0 +1,25 @@ +CREATE TABLE [dbo].[logger_disk_utilisation_database] +( + [database_name] sysname, + [database_create_date] datetime, + [database_size_bytes] bigint, + [unallocated_space_bytes] bigint, + [reserved_bytes] bigint, + [data_bytes] bigint, + [index_size_bytes] bigint, + [unused_bytes] bigint, + [log_size_total_bytes] bigint, + [log_size_used_bytes] bigint, + [snapshot_time] datetime, + [snapshot_type_id] tinyint, + constraint PK_logger_disk_util_database + primary key clustered ([snapshot_time], [database_name]), + constraint FK_logger_disk_util_database_database + foreign key ([database_name],[database_create_date]) + references [dbo].[sql_perf_mon_database] ([database_name],[database_create_date]) + on delete cascade, + constraint FK_logger_disk_util_database_snapshot + foreign key ([snapshot_time],[snapshot_type_id]) + references [dbo].[sql_perf_mon_snapshot_header] ([snapshot_time],[snapshot_type_id]) + on delete cascade +) diff --git a/SQLWATCHDB/dbo/Tables/sql_perf_mon_config_snapshot_header_type.sql b/SQLWATCHDB/dbo/Tables/sql_perf_mon_config_snapshot_type.sql similarity index 100% rename from SQLWATCHDB/dbo/Tables/sql_perf_mon_config_snapshot_header_type.sql rename to SQLWATCHDB/dbo/Tables/sql_perf_mon_config_snapshot_type.sql diff --git a/SQLWATCHDB/dbo/Tables/sql_perf_mon_database.sql b/SQLWATCHDB/dbo/Tables/sql_perf_mon_database.sql index 3eb5dca9..7f12271c 100644 --- a/SQLWATCHDB/dbo/Tables/sql_perf_mon_database.sql +++ b/SQLWATCHDB/dbo/Tables/sql_perf_mon_database.sql @@ -1,4 +1,9 @@ CREATE TABLE [dbo].[sql_perf_mon_database] ( - [database_name] sysname primary key + [database_name] sysname not null, + [database_create_date] datetime not null default '1970-01-01', + [database_current] bit not null default 1, + constraint PK_database primary key clustered ( + [database_name],[database_create_date] + ) ) diff --git a/SQLWATCHDB/dbo/Tables/sql_perf_mon_master_files.sql b/SQLWATCHDB/dbo/Tables/sql_perf_mon_master_files.sql new file mode 100644 index 00000000..7cf8249a --- /dev/null +++ b/SQLWATCHDB/dbo/Tables/sql_perf_mon_master_files.sql @@ -0,0 +1,21 @@ +CREATE TABLE [dbo].[sql_perf_mon_master_files] +( + [database_name] sysname, + [database_create_date] datetime, + [file_type] tinyint, + [file_physical_name] nvarchar(260), + [file_size] int, + [snapshot_time] datetime, + [snapshot_type_id] tinyint, + constraint PK_sql_perf_mon_master_files primary key clustered ( + [snapshot_time], [database_name] + ), + constraint FK_sql_perf_mon_master_files_db foreign key ([database_name], [database_create_date]) + references [dbo].[sql_perf_mon_database]( + [database_name], [database_create_date] + ) on delete cascade, + constraint FK_sql_perf_mon_master_files_snapshot foreign key ([snapshot_time], [snapshot_type_id]) + references [dbo].[sql_perf_mon_snapshot_header] ( + [snapshot_time], [snapshot_type_id] + ) on delete cascade +)