Technical Article

SQL Server System Report

,

There's a lot of information that can be retrieved from an instance of SQL Server; some of it you will find helpful, some of it you won't (it all depends on your environment, needs, and perspective). I've found, however, that there are certain key elements that I want to be kept up to date with, either as a way to spot potential problems or just for my own personal knowledge. With that in mind, I decided to construct a general "System Report" procedure that I could run either on-the-fly and display the results on-screen, or deploy and schedule on any instance and have the results automatically delivered to my inbox.

If you wish to output to the screen, simply type and run:

EXECUTE dbo.usp_SQL_Server_System_Report

There are 14 optional input parameters:

  1. @Output_Mode: defaulted to NULL, you can pass a value of 'E' to have the results sent via e-mail (see below for further details)
  2. @Unused_Index_Days: defaulted to 7, this is the number of days the server has to have been online (since the last SQL Server startup) before the report will execute the "Unused Indexes" section of the code (see below for further details)
  3. @Recipients: defaulted to NULL, e-mail address(es) to send the report to (separated by semicolons if there's more than one) when @Output_Mode is set to 'E'
  4. @Copy_Recipients: defaulted to NULL, e-mail address(es) to CC the report to (separated by semicolons if there's more than one) when @Output_Mode is set to 'E'
  5. @Instance_Information: defaulted to 1, enables or disables the "Instance Information" section of the report (0 = disabled, 1 = enabled)
  6. @Instance_Settings: defaulted to 1, enables or disables the "Instance Settings" section of the report (0 = disabled, 1 = enabled)
  7. @Drives_Space: defaulted to 1, enables or disables the "Fixed Drives Free Space" section of the report (0 = disabled, 1 = enabled)
  8. @Database_Summary: defaulted to 1, enables or disables the "Database Summary" section of the report (0 = disabled, 1 = enabled)
  9. @Database_Details: defaulted to 1, enables or disables the "Database Details" section of the report (0 = disabled, 1 = enabled)
  10. @Last_Backup: defaulted to 1, enables or disables the "Last Backup Set Details" section of the report (0 = disabled, 1 = enabled)
  11. @Agent_Jobs: defaulted to 1, enables or disables the "SQL Server Agent Jobs (Last 24 Hours)" section of the report (0 = disabled, 1 = enabled)
  12. @Fragmentation: defaulted to 1, enables or disables the "Index Fragmentation" section of the report (0 = disabled, 1 = enabled)
  13. @Missing_Indexes: defaulted to 1, enables or disables the "Missing Indexes" section of the report (0 = disabled, 1 = enabled)
  14. @Unused_Indexes: defaulted to 1, enables or disables the "Unused Indexes" section of the report (0 = disabled, 1 = enabled)

Additional Notes:

  • Any piece of the code which produces an empty result set will be omitted from the final report (eliminating blank report sections)
  • All numeric values use commas for thousandths separators
  • All "size" values are rounded and converted to MB
  • All "duration" values are in DAY(s) HH:MM:SS format, with any leading zeroes (when the time unit contains all zeroes) replaced by underscores for easier readability (example: a duration of 8 minutes and 12 seconds will be represented as _ Day(s) __:08:12, and a duration less than a second will be represented as _ Day(s) __:__:__)
  • If you run the procedure using the e-mail output option, the subject line of the report will automatically be set to: SQL Server System Report: "Your Server Name"

A nice part about this procedure is that it sets up the initial framework to store various data set results into permanent tables (if you wish, and are willing to add a bit of extra code) so that the results can be analyzed and trended at a later point in time.

Notes Specific To The E-Mail Output Option:

Personally, I use the e-mail output option and have it set to run daily through a SQL Server Agent Job, though your needs may differ (if you decide to use an alternate schedule then you may want to adjust the code accordingly as some stats are for the last 24 hours only and will not be very useful if you plan to run the procedure say once a week). Also, feel free to disable any sections which you feel add little or no value to your particular environment.

The end result is an HTML formatted series of data grids (with header labels) sent in the body of an e-mail via sp_send_dbmail (database mail must be set up before you can use this option!).

Report Output:

Each part of the report is listed below (e-mail / HTML output version shown), along with additional details and sample screenshots with the output field descriptions listed under each section.

Instance Information:

This is a quick overview of your system's general properties:

  • NetBIOS Name: NetBIOS name of the SQL Server
  • Server Name: name of the SQL Server
  • Instance Name: name of the SQL Server instance
  • Product Name: product name
  • Edition: product edition
  • Version: product version in the form of 'major.minor.build'
  • Level: level of the version (original, service pack, beta)
  • Clustered: indicates if the instance is configured in a failover cluster
  • Online Since: date since SQL Server last started
  • Uptime: number of days since SQL Server last started
  • Process ID: used to identify which SQLServer.exe belongs to which instance
  • Connections: shows the number of connections (successful or not) since SQL Server was last started
  • Reads: disk reads (not cache reads) since SQL Server last started
  • Writes: disk writes since SQL Server last started
  • Logical CPU Count: logical CPUs on the system
  • Physical CPU Count: physical CPUs on the system
  • Server Memory (MB): total physical memory on the server
  • Windows Version: shows the operating system and version information for the instance

Instance Settings:

This section lists some general system settings that most individuals like to be aware of:

  • Authentication: security authentication mode of the instance
  • Language: language of the instance
  • Locale ID: locale ID of the instance
  • Collation: default collation of the instance
  • Date Format: date format of the instance
  • First Day Of Week: shows what the default first day of the week is on the instance
  • Full-Text Installed: indicates if the full-text component is installed
  • Advanced Options Enabled: indicates if advanced options is enabled
  • CLR Enabled: indicates if CLR user code execution is enabled
  • Compress Backups: indicates if backups are compressed by default
  • Scan For Startup Procs: indicates if the instance scans for startup stored procedures
  • Optimize For Ad Hocs: indicates if the plan cache size has been reduced for single-use ad hoc workloads
  • Command Shell Enabled: indicates if the command shell is enabled
  • SQL Mail Enabled: indicates if SQL mail is enabled (depends on the SQL Server version)
  • Database Mail Enabled: indicates if database mail is enabled
  • Default Trace Enabled: indicates if the default trace is enabled
  • Fill Factor %: default fill factor percentage
  • Minimum Memory (MB): shows the minimum memory allocated to SQL Server
  • Maximum Memory (MB): shows the maximum memory allocated to SQL Server

Fixed Drives Free Space:

Listing of the fixed drives attached to the server, along with the free space available on each:

  • Drive Letter: letter assigned to the drive
  • Free Space (MB): free space remaining on the drive

Database Summary:

Listing of each database's size stats, along with how the space has been distributed / allocated.

NOTE: this only displays for databases which are online, set to read / write mode (not read-only), are not in standby mode, and are not a database snapshot:

  • Database Name: name of the database
  • Database Owner: name of the database owner
  • Recovery Model: recovery model the database has been set to
  • Compatibility: SQL Server compatibility behavior
  • Create Date: date the database was created (or renamed)
  • Collation: default collation of the database
  • Case Sensitive: indicates if the database is case sensitive
  • Total Size (MB): total size of the database (includes both data and log files)
  • Unallocated (MB): space in the database that has not been reserved for database objects
  • Reserved (MB): space allocated by objects in the database
  • Data (MB): space used by data
  • Index (MB): space used by indexes
  • Unused (MB): space reserved for objects in the database but not yet used
  • Cached (MB): space used by cached data pages

Database Details:

A more detailed view of the database space allocation, with suppression of duplicates on the Database Name column for easy readability, along with some general database attributes:

  • Database Name: name of the database
  • File Type: type of file (rows, log, full-text)
  • Logical Name: logical name of the database file
  • Drive Letter: drive letter where the physical file is located
  • File Path: file path where the physical file is located
  • File Name: physical name of the database file
  • File Size (MB): size of the file
  • Max Size: maximum size the file can grow to
  • Growth Increment: increment by which the file will grow when an auto-growth operation occurs
  • File Reads: number of reads issued on the file
  • File Writes: number of writes issued on the file
  • File Read (MB): total megabytes read from this file
  • File Written (MB): total megabytes written to this file
  • I/O Wait Time Reads: total time users waited for reads to be issued on the database file
  • I/O Wait Time Writes: total time users waited for writes to be completed on the database file

Last Backup Set Details:

Details surrounding the last set of backups to run for all backup types (will only list databases currently connected to the instance and will also include databases which have never been backed up). Includes suppression of duplicates on the Database Name column for easy readability:

  • Database Name: name of the database
  • Backup Set ID: backup set identification number
  • Backup Type: type of backup performed (database, log, differential, etc.)
  • Software Version: product version in the form of 'major.minor.build'
  • Database Version: database version number
  • Compatibility: compatibility level of the database
  • Server Name: name of the server which created the backup
  • Machine Name: name of the machine which created the backup
  • Physical Device Name: physical name of the backup device
  • Backup Start Date: start date of the last backup operation
  • Backup Finish Date: finish date of the last backup operation
  • Duration: time taken for the backup to execute
  • Backup Size (MB): size of the backup set
  • Compressed Size (MB): size of the backup set compressed
  • Compression %: compression ratio (may not be exact due to rounding issues)
  • Days Ago: number of days since the backup was last executed

SQL Server Agent Jobs (Last 24 Hours):

List of all SQL Server Agent Jobs executed in the last 24 hours (either scheduled or manually):

  • Job Name: name of the job
  • Job Owner: name of the job owner
  • Date Created: date the job was created
  • Date Modified: date the job was last modified
  • Version: version of the job
  • Category: job category name
  • Class: type of item in the category
  • Type: type of category
  • Last Run Date / Time: date the job was last executed
  • Last Status: last execution status of the job
  • Duration: time taken to execute the job
  • Next Run Date / Time: next scheduled execution of the job (if any)
  • Days Away: number of days until the next scheduled execution of the job (if applicable)

Index Fragmentation:

List all indexes in the database with a fragmentation level of 5% or higher (uses the sys.dm_db_index_physical_stats dynamic management function in LIMITED mode).

NOTE: this only displays for databases which are online, set to read / write mode (not read-only), are not in standby mode, and are not a database snapshot:

  • Object Type: indicates the type of object (will either be "U" for User-Defined Table or "V" for View)
  • Database Name: name of the database
  • Schema Name: name of the schema
  • Object Name: name of the object
  • Index Name: name of the index
  • Index Key: list of columns which comprise the index
  • Include Key: list of columns which comprise the include portion of the index
  • Filter Definition: filtered index definition / criteria
  • Fragmentation: average logical index fragmentation percent
  • Index Type: type of index (clustered, nonclustered, heap, etc.)
  • PK: indicates if the index is a primary key
  • Unique: indicates if the index is a unique constraint
  • Recommendation: recommendation based on index fragmentation level (5-30% = Reorganize, > 30% = Rebuild)
  • Row Count: total table / view row count
  • Alter Index Statement: SQL statement to reorganize or rebuild the index

Missing Indexes:

List missing indexes which could help improve query performance. Be very careful when deciding which new indexes to add. In some cases I've had SQL Server suggest adding a ton of columns to the index and a bunch more to the include portion. In the end it's essentially every column from the table. Also, consider the overall impact the index will make, along with the number of unique compiles. Choosing which indexes to implement is truly an art form, so please use caution and common sense.

Lastly, please note that the Create Index Statement output will attempt to name your indexes for you. You can change this to whatever format suits your needs, but be aware that if the attempted name exceeds the maximum length limit a generic value of "<INDEX NAME>" will be substituted and you will need to manually enter a name yourself.

NOTE: this only displays for databases which are online, set to read / write mode (not read-only), are not in standby mode, and are not a database snapshot:

  • Object Type: indicates the type of object (will either be "U" for User-Defined Table or "V" for View)
  • Database Name: name of the database
  • Schema Name: name of the schema
  • Object Name: name of the object
  • Unique Compiles: compiles / recompiles that could have benefitted from the missing index
  • User Seeks: number of user seeks that could have used the missing index instead
  • User Scans: number of user scans that could have used the missing index instead
  • Avg User Cost: current average cost of the user queries
  • Avg User Impact: average percent benefit that user queries could experience if the missing index is added
  • Overall Impact: calculation based on (User Seeks + User Scans) * (Avg User Cost * Avg User Impact)
  • Impact Rank: rank (per database) based on Overall Impact
  • Index Key: suggested columns which would comprise the index
  • Include Key: suggested columns which would comprise the include / covering portion of the index
  • Table Column Count: total columns in the table
  • Index Column Count: total columns which would be part of the index
  • Include Column Count: total columns which would be part of the include
  • Index % Of Columns: total percent of columns that would make up the index as compared to the total columns in the table
  • Include % Of Columns: total percent of columns that would make up the include as compared to the total columns in the table
  • Total % Of Columns: total percent of columns that would make up the entire index (index and include) as compared to the total columns in the table
  • Row Count: total table / view row count
  • Create Index Statement: SQL statement to create the index

Unused Indexes:

List of all unused indexes per database. By default, this section of code will not run until at least 7 days have passed since the server was last started (allowing for a decent amount of index statistics to be collected). You can change this number by passing a different value to the @Unused_Index_Days input parameter if you feel this is too low or too high for your particular environment. The output includes "drop index" statements so you can easily update your tables, though each result should be carefully examined before any decision is made to run the code. It only checks indexes on tables which are not MS shipped, are not primary or foreign keys, and are not unique constraints.

NOTE: this only displays for databases which are online, set to read / write mode (not read-only), are not in standby mode, and are not a database snapshot:

  • Object Type: indicates the type of object (will either be "U" for User-Defined Table or "V" for View)
  • Database Name: name of the database
  • Schema Name: name of the schema
  • Object Name: name of the object
  • Index Name: name of the index
  • Index Key: list of columns which comprise the index
  • Include Key: list of columns which comprise the include portion of the index
  • Filter Definition: filtered index definition / criteria
  • Disabled: indicates if the index is disabled
  • Hypothetical: indicates if the index is hypothetical
  • Row Count: total table / view row count
  • Drop Index Statement: SQL statement to drop the index

Any friendly feedback is always welcome. Enjoy!

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
SET NOCOUNT ON


-----------------------------------------------------------------------------------------------------------------------------
--	Error Trapping: Check If Procedure Already Exists And Create Shell If Applicable
-----------------------------------------------------------------------------------------------------------------------------

IF OBJECT_ID (N'dbo.usp_SQL_Server_System_Report', N'P') IS NULL
BEGIN

	EXECUTE ('CREATE PROCEDURE dbo.usp_SQL_Server_System_Report AS SELECT 1 AS shell')

END
GO


-----------------------------------------------------------------------------------------------------------------------------
--	Stored Procedure Details: Listing Of Standard Details Related To The Stored Procedure
-----------------------------------------------------------------------------------------------------------------------------

--	Purpose: SQL Server System Report
--	Create Date (MM/DD/YYYY): 05/12/2010
--	Developer: Sean Smith (s.smith.sql AT gmail DOT com)
--	Additional Notes: N/A


-----------------------------------------------------------------------------------------------------------------------------
--	Modification History: Listing Of All Modifications Since Original Implementation
-----------------------------------------------------------------------------------------------------------------------------

--	Description: Added "@Output_Mode" And "@Unused_Index_Days" Input Parameters
--	           : Code Restructuring For Better Query Performance
--	           : Minor Changes To Code Style
--	           : Added "connections""first_day_of_week", "is_clustered", And "windows_version" To "Instance Information" Section
--	Date (MM/DD/YYYY): 12/22/2011
--	Developer: Sean Smith (s.smith.sql AT gmail DOT com)
--	Additional Notes: N/A


--	Description: Added "create_date" And "file_name" To "Database Details" Section
--	           : Added "backup_finish_date", "database_version", "machine_name", And "server_name" To "Last Backup Set Details" Section
--	           : "Last Backup Set Details" Section Now Only Shows Information For Databases Currently On The Instance
--	           : Added "schema_name" To "Unused Indexes" Section
--	           : Extended "CONVERT" Character Length To Deal With "Msg 234" Error
--	           : Rewrote Time Calculation Logic
--	Date (MM/DD/YYYY): 01/18/2012
--	Developer: Sean Smith (s.smith.sql AT gmail DOT com)
--	Additional Notes: N/A


--	Description: Added Additional Input Parameters
--	           : Reformatted Code
--	           : Bug Fixes
--	           : Added "Instance Settings", "Index Fragmentation", "Missing Indexes" Sections
--	Date (MM/DD/YYYY): 09/20/2013
--	Developer: Sean Smith (s.smith.sql AT gmail DOT com)
--	Additional Notes: N/A


--	Description: Renamed Some Headings / Variables / Temp Tables
--	           : Bug Fixes
--	           : Enhanced HTML Output
--	           : "Index" Ouputs Now Include Indexed Views
--	           : Added "instance_name", "product_name", And "server_memory_mb" To "Instance Information" Section
--	           : Added "authentication", "date_format", "fill_factor_percentage", "is_compressed_backups_enabled", "is_optimize_for_ad_hoc_workloads_enabled""is_scan_for_startup_procs_enabled", "is_sql_mail_enabled", "language", And "locale_id" To "Instance Settings" Section
--	           : Added "cached_mb" To "Database Summary" Section
--	           : Added "database_owner", "drive_letter", "file_name", "file_path", "file_read_mb", "file_reads", "file_writes", "file_written_mb", "io_wait_time_reads", And "io_wait_time_writes" To "Database Details" Section
--	           : Added "physical_device_name" To "Last Backup Set Details" Section
--	           : Added "category", "class", "date_created", "date_modified", "job_owner", "type", And "version" To "SQL Server Agent Jobs (Last 24 Hours)" Section
--	           : Added "filter_definition", "include_key", "object_type", And "row_count" To "Index Fragmentation" Section
--	           : Added "object_type" And "row_count" To "Missing Indexes" Section
--	           : Added "filter_definition", "include_key", "object_type", And "row_count" To "Unused Indexes" Section
--	Date (MM/DD/YYYY): 12/11/2013
--	Developer: Sean Smith (s.smith.sql AT gmail DOT com)
--	Additional Notes: N/A


--	Description: Major Revamp Of "Database Summary" And "Database Details" Sections
--	Date (MM/DD/YYYY): 07/11/2014
--	Developer: Sean Smith (s.smith.sql AT gmail DOT com)
--	Additional Notes: N/A


-----------------------------------------------------------------------------------------------------------------------------
--	Main Query: Create Procedure
-----------------------------------------------------------------------------------------------------------------------------

ALTER PROCEDURE dbo.usp_SQL_Server_System_Report

	 @Output_Mode AS CHAR (1) = NULL
	,@Unused_Index_Days AS INT = 7
	,@Recipients AS VARCHAR (MAX) = NULL
	,@Copy_Recipients AS VARCHAR (MAX) = NULL
	,@Instance_Information AS BIT = 1
	,@Instance_Settings AS BIT = 1
	,@Drives_Space AS BIT = 1
	,@Database_Summary AS BIT = 1
	,@Database_Details AS BIT = 1
	,@Last_Backup AS BIT = 1
	,@Agent_Jobs AS BIT = 1
	,@Fragmentation AS BIT = 1
	,@Missing_Indexes AS BIT = 1
	,@Unused_Indexes AS BIT = 1

AS

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
SET NOCOUNT ON
SET ANSI_WARNINGS OFF
SET ARITHABORT OFF
SET ARITHIGNORE ON
SET TEXTSIZE 2147483647


DECLARE
	 @Avoid_Truncation AS NVARCHAR (MAX)
	,@Body AS NVARCHAR (MAX)
	,@Database_ID AS INT
	,@Database_Name_Loop AS NVARCHAR (500)
	,@Date_24_Hours_Ago AS DATETIME
	,@Date_Now AS DATETIME
	,@Filter_Definition_String AS NVARCHAR (50)
	,@Server_Memory_MB AS BIGINT
	,@SQL_Server_Start_Time AS DATETIME
	,@SQL_String AS NVARCHAR (MAX)
	,@Subject AS NVARCHAR (255)
	,@XML_String AS NVARCHAR (MAX)


DECLARE @Fixed_Drives_Free_Space AS TABLE

	(
		 drive_letter VARCHAR (5) PRIMARY KEY CLUSTERED
		,free_space_mb BIGINT
	)


SET @Avoid_Truncation = N''
SET @Body = N''
SET @Date_24_Hours_Ago = GETDATE () - 1
SET @Date_Now = @Date_24_Hours_Ago + 1
SET @Subject = N'SQL Server System Report: ' + @@SERVERNAME
SET @XML_String = N''


IF EXISTS (SELECT * FROM master.sys.all_columns AC WHERE AC.[object_id] = OBJECT_ID (N'master.sys.dm_os_sys_info', N'V') AND AC.name = N'sqlserver_start_time')
BEGIN

	SET @SQL_String =

		N'
			SELECT
				@SQL_Server_Start_Time = DOSI.sqlserver_start_time
			FROM
				master.sys.dm_os_sys_info DOSI
		 '


	EXECUTE sys.sp_executesql

		 @SQL_String
		,N'
			@SQL_Server_Start_Time AS DATETIME OUTPUT
		  '
		,@SQL_Server_Start_Time OUTPUT

END
ELSE BEGIN

	SET @SQL_Server_Start_Time =

		(
			SELECT
				DB.create_date
			FROM
				master.sys.databases DB
			WHERE
				DB.name = N'tempdb'
		)

END


IF DATEDIFF (SECOND, @SQL_Server_Start_Time, GETDATE ()) < (@Unused_Index_Days * 86400)
BEGIN

	SET @Unused_Indexes = 0

END


IF EXISTS (SELECT * FROM master.sys.all_columns AC WHERE AC.[object_id] = OBJECT_ID (N'master.sys.dm_os_sys_info', N'V') AND AC.name = N'physical_memory_kb')
BEGIN

	SET @SQL_String =

		N'
			SELECT
				@Server_Memory_MB = DOSI.physical_memory_kb / 1024
			FROM
				master.sys.dm_os_sys_info DOSI
		 '

END
ELSE BEGIN

	SET @SQL_String =

		N'
			SELECT
				@Server_Memory_MB = DOSI.physical_memory_in_bytes / 1048576
			FROM
				master.sys.dm_os_sys_info DOSI
		 '

END


EXECUTE sys.sp_executesql

	 @SQL_String
	,N'
		@Server_Memory_MB AS BIGINT OUTPUT
	  '
	,@Server_Memory_MB OUTPUT


IF EXISTS (SELECT * FROM master.sys.all_columns AC WHERE AC.[object_id] = OBJECT_ID (N'master.sys.indexes', N'V') AND AC.name = N'filter_definition')
BEGIN

	SET @Filter_Definition_String = N'ISNULL (I.filter_definition, N'''')'

END
ELSE BEGIN

	SET @Filter_Definition_String = N''''''

END


-----------------------------------------------------------------------------------------------------------------------------
--	Error Trapping: Check If Temp Table(s) Already Exist(s) And Drop If Applicable
-----------------------------------------------------------------------------------------------------------------------------

IF OBJECT_ID (N'tempdb.dbo.#temp_sssr_databases', N'U') IS NOT NULL
BEGIN

	DROP TABLE dbo.#temp_sssr_databases

END


IF OBJECT_ID (N'tempdb.dbo.#temp_sssr_row_counts', N'U') IS NOT NULL
BEGIN

	DROP TABLE dbo.#temp_sssr_row_counts

END


IF OBJECT_ID (N'tempdb.dbo.#temp_sssr_instance_information', N'U') IS NOT NULL
BEGIN

	DROP TABLE dbo.#temp_sssr_instance_information

END


IF OBJECT_ID (N'tempdb.dbo.#temp_sssr_instance_settings', N'U') IS NOT NULL
BEGIN

	DROP TABLE dbo.#temp_sssr_instance_settings

END


IF OBJECT_ID (N'tempdb.dbo.#temp_sssr_database_summary', N'U') IS NOT NULL
BEGIN

	DROP TABLE dbo.#temp_sssr_database_summary

END


IF OBJECT_ID (N'tempdb.dbo.#temp_sssr_database_details', N'U') IS NOT NULL
BEGIN

	DROP TABLE dbo.#temp_sssr_database_details

END


IF OBJECT_ID (N'tempdb.dbo.#temp_sssr_last_backup_set', N'U') IS NOT NULL
BEGIN

	DROP TABLE dbo.#temp_sssr_last_backup_set

END


IF OBJECT_ID (N'tempdb.dbo.#temp_sssr_agent_jobs', N'U') IS NOT NULL
BEGIN

	DROP TABLE dbo.#temp_sssr_agent_jobs

END


IF OBJECT_ID (N'tempdb.dbo.#temp_sssr_index_fragmentation', N'U') IS NOT NULL
BEGIN

	DROP TABLE dbo.#temp_sssr_index_fragmentation

END


IF OBJECT_ID (N'tempdb.dbo.#temp_sssr_missing_indexes', N'U') IS NOT NULL
BEGIN

	DROP TABLE dbo.#temp_sssr_missing_indexes

END


IF OBJECT_ID (N'tempdb.dbo.#temp_sssr_unused_indexes', N'U') IS NOT NULL
BEGIN

	DROP TABLE dbo.#temp_sssr_unused_indexes

END


-----------------------------------------------------------------------------------------------------------------------------
--	Temp Table: Database List
-----------------------------------------------------------------------------------------------------------------------------

CREATE TABLE dbo.#temp_sssr_databases

	(
		 database_name NVARCHAR (128) PRIMARY KEY CLUSTERED
		,database_owner NVARCHAR (128)
		,recovery_model NVARCHAR (60)
		,[compatibility_level] VARCHAR (3)
		,create_date DATETIME
		,collation_name NVARCHAR (128)
	)


INSERT INTO dbo.#temp_sssr_databases

	(
		 database_name
		,database_owner
		,recovery_model
		,[compatibility_level]
		,create_date
		,collation_name
	)

SELECT
	 DB.name AS database_name
	,SUSER_SNAME (DB.owner_sid) AS database_owner
	,DB.recovery_model_desc AS recovery_model
	,DB.[compatibility_level]
	,DB.create_date
	,DB.collation_name
FROM
	master.sys.databases DB
WHERE
	DB.[state] = 0
	AND DB.is_read_only = 0
	AND DB.is_in_standby = 0
	AND DB.source_database_id IS NULL


-----------------------------------------------------------------------------------------------------------------------------
--	Temp Table: Row Counts Per Table Per Database
-----------------------------------------------------------------------------------------------------------------------------

IF (@Fragmentation = 1 OR @Missing_Indexes = 1 OR @Unused_Indexes = 1)
BEGIN

	CREATE TABLE dbo.#temp_sssr_row_counts

		(
			 database_id INT NOT NULL
			,[object_id] INT NOT NULL
			,[rows] BIGINT
		)


	SET @Database_Name_Loop =

		(
			SELECT TOP (1)
				X.database_name
			FROM
				dbo.#temp_sssr_databases X
			ORDER BY
				X.database_name
		)


	WHILE @Database_Name_Loop IS NOT NULL
	BEGIN

		SET @SQL_String = @Avoid_Truncation +

			N'
				USE [' + @Database_Name_Loop + N']


				INSERT INTO dbo.#temp_sssr_row_counts

					(
						 database_id
						,[object_id]
						,[rows]
					)

				SELECT
					 DB_ID () AS database_id
					,P.[object_id]
					,SUM (P.[rows]) AS total_rows
				FROM
					sys.partitions P
				WHERE
					P.index_id IN (0, 1)
				GROUP BY
					P.[object_id]
			 '


		EXECUTE (@SQL_String)


		SET @Database_Name_Loop =

			(
				SELECT TOP (1)
					X.database_name
				FROM
					dbo.#temp_sssr_databases X
				WHERE
					X.database_name > @Database_Name_Loop
				ORDER BY
					X.database_name
			)

	END


	ALTER TABLE dbo.#temp_sssr_row_counts ADD PRIMARY KEY CLUSTERED (database_id, [object_id])

END


-----------------------------------------------------------------------------------------------------------------------------
--	Main Query I: Instance Information
-----------------------------------------------------------------------------------------------------------------------------

IF @Instance_Information = 0
BEGIN

	GOTO Skip_Instance_Information

END


SELECT
	 SERVERPROPERTY (N'ComputerNamePhysicalNetBIOS') AS netbios_name
	,@@SERVERNAME AS server_name
	,ISNULL (SERVERPROPERTY (N'InstanceName'), N'Default Instance') AS instance_name
	,REPLACE (REPLACE (cjPNL.product_name, N' (' + cjPNL.[level] + N')', N''), N'Microsoft ', N'') AS product_name
	,REPLACE (CONVERT (NVARCHAR (128), SERVERPROPERTY (N'Edition')), N' Edition', N'') AS edition
	,SERVERPROPERTY (N'ProductVersion') AS [version]
	,cjPNL.[level]
	,(CASE SERVERPROPERTY (N'IsClustered')
		WHEN 0 THEN 'No'
		WHEN 1 THEN 'Yes'
		ELSE 'N/A'
		END) AS is_clustered
	,CONVERT (NVARCHAR (19), @SQL_Server_Start_Time, 120) AS online_since
	,(CASE
		WHEN oaDSR.total_days = '0' THEN '_'
		ELSE oaDSR.total_days
		END) + ' Day(s) ' + (CASE
								WHEN oaDSR.seconds_remaining = 0 THEN '__:__:__'
								WHEN oaDSR.seconds_remaining < 60 THEN '__:__:' + RIGHT (oaDSR.total_seconds, 2)
								WHEN oaDSR.seconds_remaining < 3600 THEN '__:' + RIGHT (oaDSR.total_seconds, 5)
								ELSE oaDSR.total_seconds
								END) AS uptime
	,SERVERPROPERTY (N'ProcessID') AS process_id
	,REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, @@CONNECTIONS), 1)), 4, 23)) AS connections
	,REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, @@TOTAL_READ), 1)), 4, 23)) AS reads
	,REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, @@TOTAL_WRITE), 1)), 4, 23)) AS writes
	,REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, DOSI.cpu_count), 1)), 4, 23)) AS logical_cpu_count
	,REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, DOSI.cpu_count / DOSI.hyperthread_ratio), 1)), 4, 23)) AS physical_cpu_count
	,REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, @Server_Memory_MB), 1)), 4, 23)) AS server_memory_mb
	,REPLACE (REPLACE (REPLACE (REPLACE (REPLACE (RIGHT (@@VERSION, CHARINDEX (REVERSE (N' on Windows '), REVERSE (@@VERSION)) - 1), N'Service Pack ', N'SP'), N'(', N''), N')', N''), N'<', N'('), N'>', N')') AS windows_version
INTO
	dbo.#temp_sssr_instance_information
FROM
	master.sys.dm_os_sys_info DOSI
	CROSS JOIN

		(
			SELECT
				 LEFT (@@VERSION, CHARINDEX (N' - ', @@VERSION) - 1) AS product_name
				,CONVERT (NVARCHAR (128), SERVERPROPERTY (N'ProductLevel')) AS [level]
		) cjPNL

	CROSS JOIN

		(
			SELECT
				DATEDIFF (SECOND, @SQL_Server_Start_Time, GETDATE ()) AS uptime_seconds
		) sqUTS

	OUTER APPLY

		(
			SELECT
				 CONVERT (VARCHAR (5), sqUTS.uptime_seconds / 86400) AS total_days
				,CONVERT (CHAR (8), DATEADD (SECOND, sqUTS.uptime_seconds % 86400, 0), 108) AS total_seconds
				,sqUTS.uptime_seconds % 86400 AS seconds_remaining
		) oaDSR


IF @@ROWCOUNT = 0
BEGIN

	GOTO Skip_Instance_Information

END


IF @Output_Mode = 'E'
BEGIN

	SET @XML_String =

		CONVERT (NVARCHAR (MAX),
			(
				SELECT
					 '',X.netbios_name AS 'td'
					,'',X.server_name AS 'td'
					,'',X.instance_name AS 'td'
					,'',X.product_name AS 'td'
					,'',X.edition AS 'td'
					,'',X.[version] AS 'td'
					,'',X.[level] AS 'td'
					,'',X.is_clustered AS 'td'
					,'',X.online_since AS 'td'
					,'',X.uptime AS 'td'
					,'',X.process_id AS 'td'
					,'','right_align' + X.connections AS 'td'
					,'','right_align' + X.reads AS 'td'
					,'','right_align' + X.writes AS 'td'
					,'','right_align' + X.logical_cpu_count AS 'td'
					,'','right_align' + X.physical_cpu_count AS 'td'
					,'','right_align' + X.server_memory_mb AS 'td'
					,'',X.windows_version AS 'td'
				FROM
					dbo.#temp_sssr_instance_information X
				FOR
					XML PATH ('tr')
			)
		)


	SET @Body = @Body +

		N'
			<br>
			<h3><center>Instance Information</center></h3>
			<center>
				<table frame=box border=1 cellpadding=2>
					<tr style="color: white; background-color: black">
						<th nowrap>NetBIOS Name</th>
						<th nowrap>Server Name</th>
						<th nowrap>Instance Name</th>
						<th nowrap>Product Name</th>
						<th nowrap>Edition</th>
						<th nowrap>Version</th>
						<th nowrap>Level</th>
						<th nowrap>Clustered</th>
						<th nowrap>Online Since</th>
						<th nowrap>Uptime</th>
						<th nowrap>Process ID</th>
						<th nowrap>Connections</th>
						<th nowrap>Reads</th>
						<th nowrap>Writes</th>
						<th nowrap>Logical CPU Count</th>
						<th nowrap>Physical CPU Count</th>
						<th nowrap>Server Memory (MB)</th>
						<th nowrap>Windows Version</th>
					</tr>
		 '


	SET @Body = @Body + @XML_String +

		N'
				</table>
			</center>
		 '

END
ELSE BEGIN

	SELECT
		 X.netbios_name
		,X.server_name
		,X.instance_name
		,X.product_name
		,X.edition
		,X.[version]
		,X.[level]
		,X.is_clustered AS [clustered]
		,X.online_since
		,X.uptime
		,X.process_id
		,X.connections
		,X.reads
		,X.writes
		,X.logical_cpu_count
		,X.physical_cpu_count
		,X.server_memory_mb
		,X.windows_version
	FROM
		dbo.#temp_sssr_instance_information X

END


Skip_Instance_Information:


IF OBJECT_ID (N'tempdb.dbo.#temp_sssr_instance_information', N'U') IS NOT NULL
BEGIN

	DROP TABLE dbo.#temp_sssr_instance_information

END


-----------------------------------------------------------------------------------------------------------------------------
--	Main Query II: Instance Settings
-----------------------------------------------------------------------------------------------------------------------------

IF @Instance_Settings = 0
BEGIN

	GOTO Skip_Instance_Settings

END


SELECT
	 (CASE SERVERPROPERTY (N'IsIntegratedSecurityOnly')
		WHEN 0 THEN 'Mixed Mode'
		WHEN 1 THEN 'Windows'
		ELSE 'N/A'
		END) AS [authentication]
	,SL.alias + N': ' + SL.name AS [language]
	,SL.lcid AS locale_id
	,SERVERPROPERTY (N'Collation') AS collation
	,SL.[dateformat] AS date_format
	,(CASE SL.[datefirst]
		WHEN 1 THEN 'Monday'
		WHEN 2 THEN 'Tuesday'
		WHEN 3 THEN 'Wednesday'
		WHEN 4 THEN 'Thursday'
		WHEN 5 THEN 'Friday'
		WHEN 6 THEN 'Saturday'
		WHEN 7 THEN 'Sunday'
		ELSE 'N/A'
		END) AS first_day_of_week
	,(CASE SERVERPROPERTY (N'IsFullTextInstalled')
		WHEN 0 THEN 'No'
		WHEN 1 THEN 'Yes'
		ELSE 'N/A'
		END) AS is_full_text_installed
	,(CASE cjCFG.is_show_advanced_options_enabled
		WHEN 0 THEN 'No'
		WHEN 1 THEN 'Yes'
		ELSE 'N/A'
		END) AS is_show_advanced_options_enabled
	,(CASE cjCFG.is_clr_enabled
		WHEN 0 THEN 'No'
		WHEN 1 THEN 'Yes'
		ELSE 'N/A'
		END) AS is_clr_enabled
	,(CASE cjCFG.is_compressed_backups_enabled
		WHEN 0 THEN 'No'
		WHEN 1 THEN 'Yes'
		ELSE 'N/A'
		END) AS is_compressed_backups_enabled
	,(CASE cjCFG.is_scan_for_startup_procs_enabled
		WHEN 0 THEN 'No'
		WHEN 1 THEN 'Yes'
		ELSE 'N/A'
		END) AS is_scan_for_startup_procs_enabled
	,(CASE cjCFG.is_optimize_for_ad_hoc_workloads_enabled
		WHEN 0 THEN 'No'
		WHEN 1 THEN 'Yes'
		ELSE 'N/A'
		END) AS is_optimize_for_ad_hoc_workloads_enabled
	,(CASE cjCFG.is_xp_cmdshell_enabled
		WHEN 0 THEN 'No'
		WHEN 1 THEN 'Yes'
		ELSE 'N/A'
		END) AS is_xp_cmdshell_enabled
	,(CASE cjCFG.is_sql_mail_enabled
		WHEN 0 THEN 'No'
		WHEN 1 THEN 'Yes'
		ELSE 'N/A'
		END) AS is_sql_mail_enabled
	,(CASE cjCFG.is_database_mail_enabled
		WHEN 0 THEN 'No'
		WHEN 1 THEN 'Yes'
		ELSE 'N/A'
		END) AS is_database_mail_enabled
	,(CASE cjCFG.is_default_trace_enabled
		WHEN 0 THEN 'No'
		WHEN 1 THEN 'Yes'
		ELSE 'N/A'
		END) AS is_default_trace_enabled
	,CONVERT (VARCHAR (3), (CASE
								WHEN cjCFG.fill_factor_percentage = 0 THEN 100
								ELSE cjCFG.fill_factor_percentage
								END)) AS fill_factor_percentage
	,REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, cjCFG.min_server_memory_mb), 1)), 4, 23)) AS min_server_memory_mb
	,REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, cjCFG.max_server_memory_mb), 1)), 4, 23)) AS max_server_memory_mb
INTO
	#temp_sssr_instance_settings
FROM
	master.sys.syslanguages SL
	CROSS JOIN

		(
			SELECT
				 MAX (CASE
						WHEN CFG.configuration_id = 518 THEN CFG.value
						END) AS is_show_advanced_options_enabled
				,MAX (CASE
						WHEN CFG.configuration_id = 1562 THEN CFG.value
						END) AS is_clr_enabled
				,MAX (CASE
						WHEN CFG.configuration_id = 1579 THEN CFG.value
						END) AS is_compressed_backups_enabled
				,MAX (CASE
						WHEN CFG.configuration_id = 1547 THEN CFG.value
						END) AS is_scan_for_startup_procs_enabled
				,MAX (CASE
						WHEN CFG.configuration_id = 1581 THEN CFG.value
						END) AS is_optimize_for_ad_hoc_workloads_enabled
				,MAX (CASE
						WHEN CFG.configuration_id = 16390 THEN CFG.value
						END) AS is_xp_cmdshell_enabled
				,MAX (CASE
						WHEN CFG.configuration_id = 16385 THEN CFG.value
						END) AS is_sql_mail_enabled
				,MAX (CASE
						WHEN CFG.configuration_id = 16386 THEN CFG.value
						END) AS is_database_mail_enabled
				,MAX (CASE
						WHEN CFG.configuration_id = 1568 THEN CFG.value
						END) AS is_default_trace_enabled
				,MAX (CASE
						WHEN CFG.configuration_id = 109 THEN CFG.value
						END) AS fill_factor_percentage
				,MAX (CASE
						WHEN CFG.configuration_id = 1543 THEN CFG.value
						END) AS min_server_memory_mb
				,MAX (CASE
						WHEN CFG.configuration_id = 1544 THEN CFG.value
						END) AS max_server_memory_mb
			FROM
				master.sys.configurations CFG
			WHERE
				CFG.configuration_id IN (109, 518, 1543, 1544, 1547, 1562, 1568, 1579, 1581, 16385, 16386, 16390)
		) cjCFG

WHERE
	SL.lcid = SERVERPROPERTY (N'LCID')


IF @@ROWCOUNT = 0
BEGIN

	GOTO Skip_Instance_Settings

END


IF @Output_Mode = 'E'
BEGIN

	SET @XML_String =

		CONVERT (NVARCHAR (MAX),
			(
				SELECT
					 '',X.[authentication] AS 'td'
					,'',X.[language] AS 'td'
					,'',X.locale_id AS 'td'
					,'',X.collation AS 'td'
					,'',X.date_format AS 'td'
					,'',X.first_day_of_week AS 'td'
					,'',X.is_full_text_installed AS 'td'
					,'',X.is_show_advanced_options_enabled AS 'td'
					,'',X.is_clr_enabled AS 'td'
					,'',X.is_compressed_backups_enabled AS 'td'
					,'',X.is_scan_for_startup_procs_enabled AS 'td'
					,'',X.is_optimize_for_ad_hoc_workloads_enabled AS 'td'
					,'',X.is_xp_cmdshell_enabled AS 'td'
					,'',X.is_sql_mail_enabled AS 'td'
					,'',X.is_database_mail_enabled AS 'td'
					,'',X.is_default_trace_enabled AS 'td'
					,'','right_align' + X.fill_factor_percentage AS 'td'
					,'','right_align' + X.min_server_memory_mb AS 'td'
					,'','right_align' + X.max_server_memory_mb AS 'td'
				FROM
					dbo.#temp_sssr_instance_settings X
				FOR
					XML PATH ('tr')
			)
		)


	SET @Body = @Body +

		N'
			<br>
			<h3><center>Instance Settings</center></h3>
			<center>
				<table frame=box border=1 cellpadding=2>
					<tr style="color: white; background-color: black">
						<th nowrap>Authentication</th>
						<th nowrap>Language</th>
						<th nowrap>Locale ID</th>
						<th nowrap>Collation</th>
						<th nowrap>Date Format</th>
						<th nowrap>First Day Of Week</th>
						<th nowrap>Full-Text Installed</th>
						<th nowrap>Advanced Options Enabled</th>
						<th nowrap>CLR Enabled</th>
						<th nowrap>Compress Backups</th>
						<th nowrap>Scan For Startup Procs</th>
						<th nowrap>Optimize For Ad Hocs</th>
						<th nowrap>Command Shell Enabled</th>
						<th nowrap>SQL Mail Enabled</th>
						<th nowrap>Database Mail Enabled</th>
						<th nowrap>Default Trace Enabled</th>
						<th nowrap>Fill Factor %</th>
						<th nowrap>Minimum Memory (MB)</th>
						<th nowrap>Maximum Memory (MB)</th>
					</tr>
		 '


	SET @Body = @Body + @XML_String +

		N'
				</table>
			</center>
		 '

END
ELSE BEGIN

	SELECT
		 X.[authentication]
		,X.[language]
		,X.locale_id
		,X.collation
		,X.date_format
		,X.first_day_of_week
		,X.is_full_text_installed AS full_text_installed
		,X.is_show_advanced_options_enabled AS advanced_options_enabled
		,X.is_clr_enabled AS clr_enabled
		,X.is_compressed_backups_enabled AS compress_backups
		,X.is_scan_for_startup_procs_enabled AS scan_for_startup_procs
		,X.is_optimize_for_ad_hoc_workloads_enabled AS optimize_for_ad_hocs
		,X.is_xp_cmdshell_enabled AS command_shell_enabled
		,X.is_sql_mail_enabled AS sql_mail_enabled
		,X.is_database_mail_enabled AS database_mail_enabled
		,X.is_default_trace_enabled AS default_trace_enabled
		,X.fill_factor_percentage AS fill_factor_pct
		,X.min_server_memory_mb AS minimum_memory_mb
		,X.max_server_memory_mb AS maximum_memory_mb
	FROM
		dbo.#temp_sssr_instance_settings X

END


Skip_Instance_Settings:


IF OBJECT_ID (N'tempdb.dbo.#temp_sssr_instance_settings', N'U') IS NOT NULL
BEGIN

	DROP TABLE dbo.#temp_sssr_instance_settings

END


-----------------------------------------------------------------------------------------------------------------------------
--	Main Query III: Fixed Drives Free Space
-----------------------------------------------------------------------------------------------------------------------------

IF @Drives_Space = 0
BEGIN

	GOTO Skip_Fixed_Drives_Free_Space

END


INSERT INTO @Fixed_Drives_Free_Space

	(
		 drive_letter
		,free_space_mb
	)

EXECUTE master.dbo.xp_fixeddrives


IF @@ROWCOUNT = 0
BEGIN

	GOTO Skip_Fixed_Drives_Free_Space

END


IF @Output_Mode = 'E'
BEGIN

	SET @XML_String =

		CONVERT (NVARCHAR (MAX),
			(
				SELECT
					 '',X.drive_letter + ':' AS 'td'
					,'','right_align' + REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, X.free_space_mb), 1)), 4, 23)) AS 'td'
				FROM
					@Fixed_Drives_Free_Space X
				ORDER BY
					X.drive_letter
				FOR
					XML PATH ('tr')
			)
		)


	SET @Body = @Body +

		N'
			<br>
			<h3><center>Fixed Drives Free Space</center></h3>
			<center>
				<table frame=box border=1 cellpadding=2>
					<tr style="color: white; background-color: black">
						<th nowrap>Drive Letter</th>
						<th nowrap>Free Space (MB)</th>
					</tr>
		 '


	SET @Body = @Body + @XML_String +

		N'
				</table>
			</center>
		 '

END
ELSE BEGIN

	SELECT
		 X.drive_letter + ':' AS drive_letter
		,REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, X.free_space_mb), 1)), 4, 23)) AS free_space_mb
	FROM
		@Fixed_Drives_Free_Space X
	ORDER BY
		X.drive_letter

END


Skip_Fixed_Drives_Free_Space:


-----------------------------------------------------------------------------------------------------------------------------
--	Main Query IV: Database Summary
-----------------------------------------------------------------------------------------------------------------------------

IF @Database_Summary = 0
BEGIN

	GOTO Skip_Database_Summary

END


CREATE TABLE dbo.#temp_sssr_database_summary

	(
		 database_name NVARCHAR (128)
		,database_owner NVARCHAR (128)
		,recovery_model NVARCHAR (60)
		,[compatibility_level] VARCHAR (3)
		,create_date NVARCHAR (19)
		,collation_name NVARCHAR (128)
		,is_case_sensitive VARCHAR (3)
		,total_size_mb VARCHAR (23)
		,unallocated_mb VARCHAR (23)
		,reserved_mb VARCHAR (23)
		,data_mb VARCHAR (23)
		,index_mb VARCHAR (23)
		,unused_mb VARCHAR (23)
		,cached_mb VARCHAR (23)
	)


SET @Database_Name_Loop =

	(
		SELECT TOP (1)
			X.database_name
		FROM
			dbo.#temp_sssr_databases X
		ORDER BY
			X.database_name
	)


WHILE @Database_Name_Loop IS NOT NULL
BEGIN

	SET @SQL_String = @Avoid_Truncation +

		N'
			USE [' + @Database_Name_Loop + N']


			INSERT INTO dbo.#temp_sssr_database_summary

				(
					 database_name
					,database_owner
					,recovery_model
					,[compatibility_level]
					,create_date
					,collation_name
					,is_case_sensitive
					,total_size_mb
					,unallocated_mb
					,reserved_mb
					,data_mb
					,index_mb
					,unused_mb
				)

			SELECT
				 X.database_name
				,ISNULL (X.database_owner, N''???'') AS database_owner
				,X.recovery_model
				,ISNULL (CONVERT (VARCHAR (3), X.[compatibility_level]), ''N/A'') AS [compatibility_level]
				,CONVERT (NVARCHAR (19), X.create_date, 120) AS create_date
				,X.collation_name
				,(CASE
					WHEN ''a'' = ''A'' THEN ''No''
					ELSE ''Yes''
					END) AS is_case_sensitive
				,REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, ROUND ((sqDBF.total_size * CONVERT (BIGINT, 8192)) / 1048576.0, 0)), 1)), 4, 23)) AS total_size_mb
				,(CASE
					WHEN sqDBF.database_size >= cjPGS.total_pages THEN REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, ROUND (((sqDBF.database_size - cjPGS.total_pages) * CONVERT (BIGINT, 8192)) / 1048576.0, 0)), 1)), 4, 23))
					ELSE ''0''
					END) AS unallocated_mb
				,REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, ROUND ((cjPGS.total_pages * CONVERT (BIGINT, 8192)) / 1048576.0, 0)), 1)), 4, 23)) AS reserved_mb
				,REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, ROUND ((cjPGS.pages * CONVERT (BIGINT, 8192)) / 1048576.0, 0)), 1)), 4, 23)) AS data_mb
				,REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, ROUND (((cjPGS.used_pages - cjPGS.pages) * CONVERT (BIGINT, 8192)) / 1048576.0, 0)), 1)), 4, 23)) AS index_mb
				,REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, ROUND (((cjPGS.total_pages - cjPGS.used_pages) * CONVERT (BIGINT, 8192)) / 1048576.0, 0)), 1)), 4, 23)) AS unused_mb
			FROM
				dbo.#temp_sssr_databases X
				CROSS JOIN

					(
						SELECT
							 SUM (CASE
									WHEN DBF.[type] = 0 THEN DBF.size
									ELSE 0
									END) AS database_size
							,SUM (DBF.size) AS total_size
						FROM
							sys.database_files AS DBF
						WHERE
							DBF.[type] IN (0, 1)
					) sqDBF

				CROSS JOIN

					(
						SELECT
							 SUM (AU.total_pages) AS total_pages
							,SUM (AU.used_pages) AS used_pages
							,SUM (CASE
									WHEN IT.internal_type IN (202, 204) THEN 0
									WHEN AU.[type] <> 1 THEN AU.used_pages
									WHEN P.index_id <= 1 THEN AU.data_pages
									ELSE 0
									END) AS pages
						FROM
							sys.partitions P
							INNER JOIN sys.allocation_units AU ON AU.container_id = P.partition_id
							LEFT JOIN sys.internal_tables IT ON IT.[object_id] = P.[object_id]
					) cjPGS

			WHERE
				X.database_name = ''' + @Database_Name_Loop + N'''
		 '


	EXECUTE (@SQL_String)


	SET @Database_Name_Loop =

		(
			SELECT TOP (1)
				X.database_name
			FROM
				dbo.#temp_sssr_databases X
			WHERE
				X.database_name > @Database_Name_Loop
			ORDER BY
				X.database_name
		)

END


IF NOT EXISTS (SELECT * FROM dbo.#temp_sssr_database_summary X)
BEGIN

	GOTO Skip_Database_Summary

END


UPDATE
	X
SET
	X.cached_mb = sqCM.cached_mb
FROM
	dbo.#temp_sssr_database_summary X
	LEFT JOIN

		(
			SELECT
				 DB_NAME (DOBD.database_id) AS database_name
				,ISNULL (REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, (COUNT (*) * 8) / 1024.0), 1)), 4, 23)), '0.00') AS cached_mb
			FROM
				master.sys.dm_os_buffer_descriptors DOBD
			GROUP BY
				DB_NAME (DOBD.database_id)
		) sqCM ON sqCM.database_name = X.database_name


IF @Output_Mode = 'E'
BEGIN

	SET @XML_String =

		CONVERT (NVARCHAR (MAX),
			(
				SELECT
					 '',X.database_name AS 'td'
					,'',X.database_owner AS 'td'
					,'',X.recovery_model AS 'td'
					,'',X.[compatibility_level] AS 'td'
					,'',X.create_date AS 'td'
					,'',X.collation_name AS 'td'
					,'',X.is_case_sensitive AS 'td'
					,'','right_align' + X.total_size_mb AS 'td'
					,'','right_align' + X.unallocated_mb AS 'td'
					,'','right_align' + X.reserved_mb AS 'td'
					,'','right_align' + X.data_mb AS 'td'
					,'','right_align' + X.index_mb AS 'td'
					,'','right_align' + X.unused_mb AS 'td'
					,'','right_align' + X.cached_mb AS 'td'
				FROM
					dbo.#temp_sssr_database_summary X
				ORDER BY
					X.database_name
				FOR
					XML PATH ('tr')
			)
		)


	SET @Body = @Body +

		N'
			<br>
			<h3><center>Database Summary</center></h3>
			<center>
				<table frame=box border=1 cellpadding=2>
					<tr style="color: white; background-color: black">
						<th nowrap>Database Name</th>
						<th nowrap>Database Owner</th>
						<th nowrap>Recovery Model</th>
						<th nowrap>Compatibility</th>
						<th nowrap>Create Date</th>
						<th nowrap>Collation</th>
						<th nowrap>Case Sensitive</th>
						<th nowrap>Total Size (MB)</th>
						<th nowrap>Unallocated (MB)</th>
						<th nowrap>Reserved (MB)</th>
						<th nowrap>Data (MB)</th>
						<th nowrap>Index (MB)</th>
						<th nowrap>Unused (MB)</th>
						<th nowrap>Cached (MB)</th>
					</tr>
		 '


	SET @Body = @Body + @XML_String +

		N'
				</table>
			</center>
		 '

END
ELSE BEGIN

	SELECT
		 X.database_name
		,X.database_owner
		,X.recovery_model
		,X.[compatibility_level]
		,X.create_date
		,X.collation_name AS collation
		,X.is_case_sensitive AS case_sensitive
		,X.total_size_mb
		,X.unallocated_mb
		,X.reserved_mb
		,X.data_mb
		,X.index_mb
		,X.unused_mb
		,X.cached_mb
	FROM
		dbo.#temp_sssr_database_summary X
	ORDER BY
		X.database_name

END


Skip_Database_Summary:


IF OBJECT_ID (N'tempdb.dbo.#temp_sssr_database_summary', N'U') IS NOT NULL
BEGIN

	DROP TABLE dbo.#temp_sssr_database_summary

END


-----------------------------------------------------------------------------------------------------------------------------
--	Main Query V: Database Details
-----------------------------------------------------------------------------------------------------------------------------

IF @Database_Details = 0
BEGIN

	GOTO Skip_Database_Details

END


SELECT
	 DB_NAME (MF.database_id) AS database_name
	,CONVERT (NVARCHAR (10), LEFT (UPPER (MF.type_desc), 1) + LOWER (SUBSTRING (MF.type_desc, 2, 250))) AS file_type
	,MF.name AS logical_name
	,UPPER (LEFT (MF.physical_name, 2)) AS drive_letter
	,(CASE
		WHEN LEN (MF.physical_name) - LEN (REPLACE (MF.physical_name, N'\', N'')) < 2 THEN N'<Drive Root>'
		ELSE SUBSTRING (MF.physical_name, 4, LEN (MF.physical_name) - CHARINDEX (N'\', REVERSE (MF.physical_name)) - 3)
		END) AS file_path
	,RIGHT (MF.physical_name, CHARINDEX (N'\', REVERSE (MF.physical_name)) - 1) AS [file_name]
	,REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, ROUND ((MF.size * CONVERT (BIGINT, 8192)) / 1048576.0, 0)), 1)), 4, 23)) AS file_size_mb
	,RIGHT ((CASE
				WHEN MF.growth = 0 THEN 'Fixed Size'
				WHEN MF.max_size = -1 THEN 'Unrestricted'
				WHEN MF.max_size = 0 THEN 'None'
				WHEN MF.max_size = 268435456 THEN '2 TB'
				ELSE REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, ROUND ((MF.max_size * CONVERT (BIGINT, 8192)) / 1048576.0, 0)), 1)), 4, 23)) + ' MB'
				END), 15) AS max_size
	,RIGHT ((CASE
				WHEN MF.growth = 0 THEN 'N/A'
				WHEN MF.is_percent_growth = 1 THEN REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, MF.growth), 1)), 4, 23)) + ' %'
				ELSE REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, ROUND ((MF.growth * CONVERT (BIGINT, 8192)) / 1048576.0, 0)), 1)), 4, 23)) + ' MB'
				END), 15) AS growth_increment
	,ISNULL (REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, DIVFS.num_of_reads), 1)), 4, 23)), '0') AS file_reads
	,ISNULL (REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, DIVFS.num_of_writes), 1)), 4, 23)), '0') AS file_writes
	,ISNULL (REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, ROUND (DIVFS.num_of_bytes_read / 1048576.0, 0)), 1)), 4, 23)), '0.00') AS file_read_mb
	,ISNULL (REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, ROUND (DIVFS.num_of_bytes_written / 1048576.0, 0)), 1)), 4, 23)), '0.00') AS file_written_mb
	,ISNULL ((CASE
				WHEN oaDSR.total_days_reads = '0' THEN REPLICATE ('_', cjDLM.day_length_max_reads)
				ELSE REPLICATE ('0', cjDLM.day_length_max_reads - LEN (oaDSR.total_days_reads)) + oaDSR.total_days_reads
				END) + ' Day(s) ' + (CASE
										WHEN oaDSR.seconds_remaining_reads = 0 THEN '__:__:__'
										WHEN oaDSR.seconds_remaining_reads < 60 THEN '__:__:' + RIGHT (oaDSR.total_seconds_reads, 2)
										WHEN oaDSR.seconds_remaining_reads < 3600 THEN '__:' + RIGHT (oaDSR.total_seconds_reads, 5)
										ELSE oaDSR.total_seconds_reads
										END), REPLICATE ('_', cjDLM.day_length_max_reads) + ' Day(s) ' + '__:__:__') AS io_wait_time_reads
	,ISNULL ((CASE
				WHEN oaDSR.total_days_writes = '0' THEN REPLICATE ('_', cjDLM.day_length_max_writes)
				ELSE REPLICATE ('0', cjDLM.day_length_max_writes - LEN (oaDSR.total_days_writes)) + oaDSR.total_days_writes
				END) + ' Day(s) ' + (CASE
										WHEN oaDSR.seconds_remaining_writes = 0 THEN '__:__:__'
										WHEN oaDSR.seconds_remaining_writes < 60 THEN '__:__:' + RIGHT (oaDSR.total_seconds_writes, 2)
										WHEN oaDSR.seconds_remaining_writes < 3600 THEN '__:' + RIGHT (oaDSR.total_seconds_writes, 5)
										ELSE oaDSR.total_seconds_writes
										END), REPLICATE ('_', cjDLM.day_length_max_writes) + ' Day(s) ' + '__:__:__') AS io_wait_time_writes
	,ROW_NUMBER () OVER
						(
							PARTITION BY
								MF.database_id
							ORDER BY
								 MF.[type]
								,(CASE
									WHEN MF.[file_id] = 1 THEN 10
									ELSE 99
									END)
								,MF.name
						) AS database_filter_id
INTO
	dbo.#temp_sssr_database_details
FROM
	master.sys.master_files MF
	LEFT JOIN master.sys.dm_io_virtual_file_stats (NULL, NULL) DIVFS ON DIVFS.database_id = MF.database_id
		AND DIVFS.[file_id] = MF.[file_id]
	CROSS JOIN

		(
			SELECT
				 MAX (LEN ((XDIVFS.io_stall_read_ms / 1000) / 86400)) AS day_length_max_reads
				,MAX (LEN ((XDIVFS.io_stall_write_ms / 1000) / 86400)) AS day_length_max_writes
			FROM
				master.sys.dm_io_virtual_file_stats (NULL, NULL) XDIVFS
		) cjDLM

	OUTER APPLY

		(
			SELECT
				 CONVERT (VARCHAR (5), (DIVFS.io_stall_read_ms / 1000) / 86400) AS total_days_reads
				,CONVERT (CHAR (8), DATEADD (SECOND, (DIVFS.io_stall_read_ms / 1000) % 86400, 0), 108) AS total_seconds_reads
				,(DIVFS.io_stall_read_ms / 1000) % 86400 AS seconds_remaining_reads
				,CONVERT (VARCHAR (5), (DIVFS.io_stall_write_ms / 1000) / 86400) AS total_days_writes
				,CONVERT (CHAR (8), DATEADD (SECOND, (DIVFS.io_stall_write_ms / 1000) % 86400, 0), 108) AS total_seconds_writes
				,(DIVFS.io_stall_write_ms / 1000) % 86400 AS seconds_remaining_writes
		) oaDSR


IF @@ROWCOUNT = 0
BEGIN

	GOTO Skip_Database_Details

END


IF @Output_Mode = 'E'
BEGIN

	SET @XML_String =

		CONVERT (NVARCHAR (MAX),
			(
				SELECT
					 '',(CASE
							WHEN X.database_filter_id = 1 THEN X.database_name
							ELSE ''
							END) AS 'td'
					,'',X.file_type AS 'td'
					,'',X.logical_name AS 'td'
					,'',X.drive_letter AS 'td'
					,'',X.file_path AS 'td'
					,'',X.[file_name] AS 'td'
					,'','right_align' + X.file_size_mb AS 'td'
					,'','right_align' + X.max_size AS 'td'
					,'','right_align' + X.growth_increment AS 'td'
					,'','right_align' + X.file_reads AS 'td'
					,'','right_align' + X.file_writes AS 'td'
					,'','right_align' + X.file_read_mb AS 'td'
					,'','right_align' + X.file_written_mb AS 'td'
					,'',X.io_wait_time_reads AS 'td'
					,'',X.io_wait_time_writes AS 'td'
				FROM
					dbo.#temp_sssr_database_details X
				ORDER BY
					 X.database_name
					,X.database_filter_id
				FOR
					XML PATH ('tr')
			)
		)


	SET @Body = @Body +

		N'
			<br>
			<h3><center>Database Details</center></h3>
			<center>
				<table frame=box border=1 cellpadding=2>
					<tr style="color: white; background-color: black">
						<th nowrap>Database Name</th>
						<th nowrap>File Type</th>
						<th nowrap>Logical Name</th>
						<th nowrap>Drive Letter</th>
						<th nowrap>File Path</th>
						<th nowrap>File Name</th>
						<th nowrap>File Size (MB)</th>
						<th nowrap>Max Size</th>
						<th nowrap>Growth Increment</th>
						<th nowrap>File Reads</th>
						<th nowrap>File Writes</th>
						<th nowrap>File Read (MB)</th>
						<th nowrap>File Written (MB)</th>
						<th nowrap>I/O Wait Time Reads</th>
						<th nowrap>I/O Wait Time Writes</th>
					</tr>
		 '


	SET @Body = @Body + @XML_String +

		N'
				</table>
			</center>
		 '

END
ELSE BEGIN

	SELECT
		 (CASE
			WHEN X.database_filter_id = 1 THEN X.database_name
			ELSE ''
			END) AS database_name
		,X.file_type
		,X.logical_name
		,X.drive_letter
		,X.file_path
		,X.[file_name]
		,X.file_size_mb
		,X.max_size
		,X.growth_increment
		,X.file_reads
		,X.file_writes
		,X.file_read_mb
		,X.file_written_mb
		,X.io_wait_time_reads
		,X.io_wait_time_writes
	FROM
		dbo.#temp_sssr_database_details X
	ORDER BY
		 X.database_name
		,X.database_filter_id

END


Skip_Database_Details:


IF OBJECT_ID (N'tempdb.dbo.#temp_sssr_database_details', N'U') IS NOT NULL
BEGIN

	DROP TABLE dbo.#temp_sssr_database_details

END


-----------------------------------------------------------------------------------------------------------------------------
--	Main Query VI: Last Backup Set Details
-----------------------------------------------------------------------------------------------------------------------------

IF @Last_Backup = 0
BEGIN

	GOTO Skip_Last_Backup_Set

END


SELECT
	 X.database_name
	,ISNULL (CONVERT (VARCHAR (10), sqBS.backup_set_id), 'NONE') AS backup_set_id
	,(CASE sqBS.[type]
		WHEN 'D' THEN 'Database'
		WHEN 'F' THEN 'File Or Filegroup'
		WHEN 'G' THEN 'Differential File'
		WHEN 'I' THEN 'Differential Database'
		WHEN 'L' THEN 'Log'
		WHEN 'P' THEN 'Partial'
		WHEN 'Q' THEN 'Differential Partial'
		ELSE 'N/A'
		END) AS backup_type
	,ISNULL (CONVERT (VARCHAR (3), sqBS.software_major_version) + '.' + CONVERT (VARCHAR (3), sqBS.software_minor_version) + '.' + CONVERT (VARCHAR (6), sqBS.software_build_version), 'N/A') AS software_version
	,ISNULL (CONVERT (VARCHAR (10), sqBS.database_version), 'N/A') AS database_version
	,ISNULL (CONVERT (VARCHAR (3), sqBS.[compatibility_level]), 'N/A') AS [compatibility_level]
	,ISNULL (sqBS.server_name, 'N/A') AS server_name
	,ISNULL (sqBS.machine_name, 'N/A') AS machine_name
	,ISNULL (sqBS.physical_device_name, 'N/A') AS physical_device_name
	,ISNULL (CONVERT (VARCHAR (34), sqBS.backup_start_date, 120), 'N/A') AS backup_start_date
	,ISNULL (CONVERT (VARCHAR (34), sqBS.backup_finish_date, 120), 'N/A') AS backup_finish_date
	,ISNULL ((CASE
				WHEN sqBS.total_days = '0' THEN REPLICATE ('_', sqBS.day_length_max)
				ELSE REPLICATE ('0', sqBS.day_length_max - LEN (sqBS.total_days)) + sqBS.total_days
				END) + ' Day(s) ' + (CASE
										WHEN sqBS.seconds_remaining = 0 THEN '__:__:__'
										WHEN sqBS.seconds_remaining < 60 THEN '__:__:' + RIGHT (sqBS.total_seconds, 2)
										WHEN sqBS.seconds_remaining < 3600 THEN '__:' + RIGHT (sqBS.total_seconds, 5)
										ELSE sqBS.total_seconds
										END), 'N/A') AS duration
	,ISNULL (REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, ROUND (sqBS.backup_size / 1048576.0, 0)), 1)), 4, 23)), 'N/A') AS backup_size_mb
	,CONVERT (VARCHAR (23), 'N/A') AS compressed_size_mb
	,CONVERT (VARCHAR (23), 'N/A') AS compression_pct
	,ISNULL (REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, DATEDIFF (DAY, sqBS.backup_start_date, GETDATE ())), 1)), 4, 23)), 'N/A') AS days_ago
	,ROW_NUMBER () OVER
						(
							PARTITION BY
								X.database_name
							ORDER BY
								sqBS.[type]
						) AS database_filter_id
INTO
	dbo.#temp_sssr_last_backup_set
FROM
	dbo.#temp_sssr_databases X
	LEFT JOIN

		(
			SELECT
				 BS.database_name
				,BS.backup_set_id
				,BS.[type]
				,BS.software_major_version
				,BS.software_minor_version
				,BS.software_build_version
				,BS.database_version
				,BS.[compatibility_level]
				,BS.server_name
				,BS.machine_name
				,BMF.physical_device_name
				,BS.backup_start_date
				,BS.backup_finish_date
				,BS.backup_size
				,cjDLM.day_length_max
				,oaDSR.seconds_remaining
				,oaDSR.total_days
				,oaDSR.total_seconds
			FROM
				msdb.dbo.backupset BS
				INNER JOIN msdb.dbo.backupmediafamily BMF ON BMF.media_set_id = BS.media_set_id
				INNER JOIN

					(
						SELECT
							MAX (XBS.backup_set_id) AS backup_set_id_max
						FROM
							msdb.dbo.backupset XBS
						GROUP BY
							 XBS.database_name
							,XBS.[type]
					) sqMBS ON sqMBS.backup_set_id_max = BS.backup_set_id

				CROSS JOIN

					(
						SELECT
							MAX (LEN (DATEDIFF (DAY, YBS.backup_start_date, YBS.backup_finish_date))) AS day_length_max
						FROM
							msdb.dbo.backupset YBS
					) cjDLM

				OUTER APPLY

					(
						SELECT
							DATEDIFF (SECOND, BS.backup_start_date, BS.backup_finish_date) AS duration_seconds
					) oaDS

				OUTER APPLY

					(
						SELECT
							 CONVERT (VARCHAR (5), oaDS.duration_seconds / 86400) AS total_days
							,CONVERT (CHAR (8), DATEADD (SECOND, oaDS.duration_seconds % 86400, 0), 108) AS total_seconds
							,oaDS.duration_seconds % 86400 AS seconds_remaining
					) oaDSR

		) sqBS ON sqBS.database_name = X.database_name

WHERE
	X.database_name <> N'tempdb'


IF @@ROWCOUNT = 0
BEGIN

	GOTO Skip_Last_Backup_Set

END


IF EXISTS (SELECT * FROM msdb.sys.all_columns AC WHERE AC.[object_id] = OBJECT_ID (N'msdb.dbo.backupset', N'U') AND AC.name = N'compressed_backup_size')
BEGIN

	EXECUTE

		(
			N'
				UPDATE
					X
				SET
					 X.compressed_size_mb = REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, ROUND (BS.compressed_backup_size / 1048576.0, 0)), 1)), 4, 23))
					,X.compression_pct = CONVERT (VARCHAR (23), CONVERT (MONEY, ROUND (((BS.backup_size - BS.compressed_backup_size + .0) / BS.backup_size) * 100, 2)), 1)
				FROM
					dbo.#temp_sssr_last_backup_set X
					INNER JOIN msdb.dbo.backupset BS ON BS.backup_set_id = X.backup_set_id
						AND BS.compressed_backup_size < BS.backup_size
				WHERE
					X.backup_set_id <> ''NONE''
			 '
		)

END


IF EXISTS (SELECT * FROM dbo.#temp_sssr_last_backup_set X WHERE X.backup_set_id = 'NONE')
BEGIN

	UPDATE
		dbo.#temp_sssr_last_backup_set
	SET
		 backup_type = REPLICATE ('.', backup_type_length_max * 2)
		,software_version = REPLICATE ('.', software_version_length_max * 2)
		,database_version = REPLICATE ('.', database_version_length_max * 2)
		,[compatibility_level] = REPLICATE ('.', compatibility_level_length_max * 2)
		,server_name = REPLICATE ('.', server_name_length_max * 2)
		,machine_name = REPLICATE ('.', machine_name_length_max * 2)
		,physical_device_name = REPLICATE ('.', physical_device_name_length_max * 2)
		,backup_start_date = REPLICATE ('.', 34)
		,backup_finish_date = REPLICATE ('.', 34)
		,duration = REPLICATE ('.', (duration_length_max * 2) - 4)
		,backup_size_mb = REPLICATE ('.', backup_size_mb_length_max * 2)
		,compressed_size_mb = REPLICATE ('.', compressed_size_mb_length_max * 2)
		,compression_pct = REPLICATE ('.', compression_pct_length_max * 2)
		,days_ago = REPLICATE ('.', days_ago_length_max * 2)
	FROM

		(
			SELECT
				 MAX (LEN (X.backup_type)) AS backup_type_length_max
				,MAX (LEN (X.software_version)) AS software_version_length_max
				,MAX (LEN (X.database_version)) AS database_version_length_max
				,MAX (LEN (X.[compatibility_level])) AS compatibility_level_length_max
				,MAX (LEN (X.server_name)) AS server_name_length_max
				,MAX (LEN (X.machine_name)) AS machine_name_length_max
				,MAX (LEN (X.physical_device_name)) AS physical_device_name_length_max
				,MAX (LEN (X.duration)) AS duration_length_max
				,MAX (LEN (X.backup_size_mb)) AS backup_size_mb_length_max
				,MAX (LEN (X.compressed_size_mb)) AS compressed_size_mb_length_max
				,MAX (LEN (X.compression_pct)) AS compression_pct_length_max
				,MAX (LEN (X.days_ago)) AS days_ago_length_max
			FROM
				dbo.#temp_sssr_last_backup_set X
		) sqX

	WHERE
		dbo.#temp_sssr_last_backup_set.backup_set_id = 'NONE'

END


IF @Output_Mode = 'E'
BEGIN

	SET @XML_String =

		CONVERT (NVARCHAR (MAX),
			(
				SELECT
					 '',(CASE
							WHEN X.database_filter_id = 1 THEN X.database_name
							ELSE ''
							END) AS 'td'
					,'',X.backup_set_id AS 'td'
					,'',X.backup_type AS 'td'
					,'',X.software_version AS 'td'
					,'',X.database_version AS 'td'
					,'',X.[compatibility_level] AS 'td'
					,'',X.server_name AS 'td'
					,'',X.machine_name AS 'td'
					,'',X.physical_device_name AS 'td'
					,'',X.backup_start_date AS 'td'
					,'',X.backup_finish_date AS 'td'
					,'',X.duration AS 'td'
					,'','right_align' + X.backup_size_mb AS 'td'
					,'','right_align' + X.compressed_size_mb AS 'td'
					,'','right_align' + X.compression_pct AS 'td'
					,'','right_align' + X.days_ago AS 'td'
				FROM
					dbo.#temp_sssr_last_backup_set X
				ORDER BY
					 X.database_name
					,X.database_filter_id
				FOR
					XML PATH ('tr')
			)
		)


	SET @Body = @Body +

		N'
			<br>
			<h3><center>Last Backup Set Details</center></h3>
			<center>
				<table frame=box border=1 cellpadding=2>
					<tr style="color: white; background-color: black">
						<th nowrap>Database Name</th>
						<th nowrap>Backup Set ID</th>
						<th nowrap>Backup Type</th>
						<th nowrap>Software Version</th>
						<th nowrap>Database Version</th>
						<th nowrap>Compatibility</th>
						<th nowrap>Server Name</th>
						<th nowrap>Machine Name</th>
						<th nowrap>Physical Device Name</th>
						<th nowrap>Backup Start Date</th>
						<th nowrap>Backup Finish Date</th>
						<th nowrap>Duration</th>
						<th nowrap>Backup Size (MB)</th>
						<th nowrap>Compressed Size (MB)</th>
						<th nowrap>Compression %</th>
						<th nowrap>Days Ago</th>
					</tr>
		 '


	SET @Body = @Body + @XML_String +

		N'
				</table>
			</center>
		 '

END
ELSE BEGIN

	SELECT
		 (CASE
			WHEN X.database_filter_id = 1 THEN X.database_name
			ELSE ''
			END) AS database_name
		,X.backup_set_id
		,X.backup_type
		,X.software_version
		,X.database_version
		,X.[compatibility_level]
		,X.server_name
		,X.machine_name
		,X.physical_device_name
		,X.backup_start_date
		,X.backup_finish_date
		,X.duration
		,X.backup_size_mb
		,X.compressed_size_mb
		,X.compression_pct
		,X.days_ago
	FROM
		dbo.#temp_sssr_last_backup_set X
	ORDER BY
		 X.database_name
		,X.database_filter_id

END


Skip_Last_Backup_Set:


IF OBJECT_ID (N'tempdb.dbo.#temp_sssr_last_backup_set', N'U') IS NOT NULL
BEGIN

	DROP TABLE dbo.#temp_sssr_last_backup_set

END


-----------------------------------------------------------------------------------------------------------------------------
--	Main Query VII: SQL Server Agent Jobs (Last 24 Hours)
-----------------------------------------------------------------------------------------------------------------------------

IF @Agent_Jobs = 0
BEGIN

	GOTO Skip_Agent_Jobs

END


SELECT
	 SJ.name AS job_name
	,SUSER_SNAME (SJ.owner_sid) AS job_owner
	,CONVERT (VARCHAR (19), SJ.date_created, 120) AS date_created
	,CONVERT (VARCHAR (19), SJ.date_modified, 120) AS date_modified
	,REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, SJ.version_number), 1)), 4, 23)) AS [version]
	,SC.name AS category
	,(CASE SC.category_class
		WHEN 1 THEN 'Job'
		WHEN 2 THEN 'Alert'
		WHEN 3 THEN 'Operator'
		ELSE 'N/A'
		END) AS class
	,(CASE SC.category_type
		WHEN 1 THEN 'Local'
		WHEN 2 THEN 'Multiserver'
		WHEN 3 THEN 'None'
		ELSE 'N/A'
		END) AS [type]
	,CONVERT (VARCHAR (19), caLRDT.last_run_date_time, 120) AS last_run_date_time
	,(CASE SJH.run_status
		WHEN 0 THEN 'Failed'
		WHEN 1 THEN 'Succeeded'
		WHEN 2 THEN 'Retry'
		WHEN 3 THEN 'Canceled'
		WHEN 4 THEN 'In Progress'
		END) AS last_status
	,(CASE
		WHEN SJH.run_duration = 0 THEN '__:__:__'
		WHEN LEN (SJH.run_duration) <= 2 THEN '__:__:' + RIGHT ('0' + CONVERT (VARCHAR (2), SJH.run_duration), 2)
		WHEN LEN (SJH.run_duration) <= 4 THEN '__:' + STUFF (RIGHT ('0' + CONVERT (VARCHAR (4), SJH.run_duration), 4), 3, 0, ':')
		ELSE STUFF (STUFF (RIGHT ('0' + CONVERT (VARCHAR (6), SJH.run_duration), 6), 5, 0, ':'), 3, 0, ':')
		END) AS duration
	,ISNULL ((CASE
				WHEN SJ.[enabled] = 1 THEN CONVERT (VARCHAR (19), sqNRDT.next_run_date_time, 120)
				END), '___________________') AS next_run_date_time
	,ISNULL ((CASE
				WHEN SJ.[enabled] = 1 THEN REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, DATEDIFF (DAY, GETDATE (), sqNRDT.next_run_date_time)), 1)), 4, 23))
				END), 'N/A') AS days_away
INTO
	dbo.#temp_sssr_agent_jobs
FROM
	msdb.dbo.sysjobs SJ
	INNER JOIN msdb.dbo.sysjobhistory SJH ON SJH.job_id = SJ.job_id
	INNER JOIN msdb.dbo.syscategories SC ON SC.category_id = SJ.category_id
	INNER JOIN

		(
			SELECT
				MAX (XSJ.instance_id) AS instance_id_max
			FROM
				msdb.dbo.sysjobhistory XSJ
			GROUP BY
				XSJ.job_id
		) sqIIM ON sqIIM.instance_id_max = SJH.instance_id

	LEFT JOIN

		(
			SELECT
				 SJS.job_id
				,MIN (CONVERT (DATETIME, CONVERT (VARCHAR (8), SJS.next_run_date) + ' ' + STUFF (STUFF (RIGHT ('000000' + CONVERT (VARCHAR (6), SJS.next_run_time), 6), 5, 0, ':'), 3, 0, ':'))) AS next_run_date_time
			FROM
				msdb.dbo.sysjobschedules SJS
				INNER JOIN msdb.dbo.sysschedules SS ON SS.schedule_id = SJS.schedule_id
					AND SS.[enabled] = 1
			WHERE
				SJS.next_run_date > 0
			GROUP BY
				SJS.job_id
		) sqNRDT ON sqNRDT.job_id = SJ.job_id

	CROSS APPLY

		(
			SELECT
				CONVERT (DATETIME, CONVERT (VARCHAR (8), SJH.run_date) + ' ' + STUFF (STUFF (RIGHT ('000000' + CONVERT (VARCHAR (6), SJH.run_time), 6), 5, 0, ':'), 3, 0, ':')) AS last_run_date_time
		) caLRDT

WHERE
	caLRDT.last_run_date_time >= @Date_24_Hours_Ago


IF @@ROWCOUNT = 0
BEGIN

	GOTO Skip_Agent_Jobs

END


IF @Output_Mode = 'E'
BEGIN

	SET @XML_String =

		CONVERT (NVARCHAR (MAX),
			(
				SELECT
					 '',X.job_name AS 'td'
					,'',X.job_owner AS 'td'
					,'',X.date_created AS 'td'
					,'',X.date_modified AS 'td'
					,'','right_align' + X.[version] AS 'td'
					,'',X.category AS 'td'
					,'',X.class AS 'td'
					,'',X.[type] AS 'td'
					,'',X.last_run_date_time AS 'td'
					,'',X.last_status AS 'td'
					,'',X.duration AS 'td'
					,'',X.next_run_date_time AS 'td'
					,'','right_align' + X.days_away AS 'td'
				FROM
					dbo.#temp_sssr_agent_jobs X
				ORDER BY
					X.job_name
				FOR
					XML PATH ('tr')
			)
		)


	SET @Body = @Body +

		N'
			<br>
			<h3><center>SQL Server Agent Jobs (Last 24 Hours)</center></h3>
			<center>
				<table frame=box border=1 cellpadding=2>
					<tr style="color: white; background-color: black">
						<th nowrap>Job Name</th>
						<th nowrap>Job Owner</th>
						<th nowrap>Date Created</th>
						<th nowrap>Date Modified</th>
						<th nowrap>Version</th>
						<th nowrap>Category</th>
						<th nowrap>Class</th>
						<th nowrap>Type</th>
						<th nowrap>Last Run Date / Time</th>
						<th nowrap>Last Status</th>
						<th nowrap>Duration</th>
						<th nowrap>Next Run Date / Time</th>
						<th nowrap>Days Away</th>
					</tr>
		 '


	SET @Body = @Body + @XML_String +

		N'
				</table>
			</center>
		 '

END
ELSE BEGIN

	SELECT
		 X.job_name
		,X.job_owner
		,X.date_created
		,X.date_modified
		,X.[version]
		,X.category
		,X.class
		,X.[type]
		,X.last_run_date_time
		,X.last_status
		,X.duration
		,X.next_run_date_time
		,X.days_away
	FROM
		dbo.#temp_sssr_agent_jobs X
	ORDER BY
		X.job_name

END


Skip_Agent_Jobs:


IF OBJECT_ID (N'tempdb.dbo.#temp_sssr_agent_jobs', N'U') IS NOT NULL
BEGIN

	DROP TABLE dbo.#temp_sssr_agent_jobs

END


-----------------------------------------------------------------------------------------------------------------------------
--	Main Query VIII: Index Fragmentation
-----------------------------------------------------------------------------------------------------------------------------

IF @Fragmentation = 0
BEGIN

	GOTO Skip_Index_Fragmentation

END


CREATE TABLE dbo.#temp_sssr_index_fragmentation

	(
		 object_type CHAR (2)
		,database_name NVARCHAR (128)
		,[schema_name] SYSNAME
		,[object_name] SYSNAME
		,index_name SYSNAME
		,index_key NVARCHAR (MAX)
		,include_key NVARCHAR (MAX)
		,filter_definition NVARCHAR (MAX)
		,fragmentation VARCHAR (23)
		,index_type NVARCHAR (120)
		,is_pk VARCHAR (3)
		,is_unique VARCHAR (3)
		,recommendation VARCHAR (10)
		,row_count VARCHAR (23)
		,alter_index_statement NVARCHAR (4000)
	)


SET @Database_Name_Loop =

	(
		SELECT TOP (1)
			X.database_name
		FROM
			dbo.#temp_sssr_databases X
		WHERE
			X.database_name <> N'tempdb'
		ORDER BY
			X.database_name
	)


WHILE @Database_Name_Loop IS NOT NULL
BEGIN

	SET @Database_ID = DB_ID (@Database_Name_Loop)


	SET @SQL_String = @Avoid_Truncation +

		N'
			USE [' + @Database_Name_Loop + N']


			INSERT INTO dbo.#temp_sssr_index_fragmentation

				(
					 object_type
					,database_name
					,[schema_name]
					,[object_name]
					,index_name
					,index_key
					,include_key
					,filter_definition
					,fragmentation
					,index_type
					,is_pk
					,is_unique
					,recommendation
					,row_count
					,alter_index_statement
				)

			SELECT
				 sqIF.[type] AS object_type
				,DB_NAME () AS database_name
				,SCHEMA_NAME (sqIF.[schema_id]) AS [schema_name]
				,sqIF.[object_name]
				,sqIF.index_name
				,sqIF.index_key
				,sqIF.include_key
				,sqIF.filter_definition
				,CONVERT (VARCHAR (23), CONVERT (MONEY, sqIF.avg_fragmentation_in_percent), 1) AS fragmentation
				,sqIF.type_desc AS index_type
				,(CASE sqIF.is_primary_key
					WHEN 0 THEN ''No''
					WHEN 1 THEN ''Yes''
					ELSE ''N/A''
					END) AS is_pk
				,(CASE sqIF.is_unique
					WHEN 0 THEN ''No''
					WHEN 1 THEN ''Yes''
					ELSE ''N/A''
					END) AS is_unique
				,caREC.recommendation
				,REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, sqIF.[rows]), 1)), 4, 23)) AS row_count
				,N''USE [''
				 + DB_NAME ()
				 + N'']; ALTER INDEX [''
				 + sqIF.index_name
				 + N''] ON [''
				 + SCHEMA_NAME (sqIF.[schema_id])
				 + N''].[''
				 + sqIF.[object_name]
				 + N''] ''
				 + caREC.recommendation
				 + (CASE caREC.recommendation
						WHEN ''REBUILD'' THEN N'' WITH (MAXDOP = 1)''
						ELSE N''''
						END)
				 + N'';'' AS alter_index_statement
			FROM

				(
					SELECT
						 O.[type]
						,O.[schema_id]
						,O.name AS [object_name]
						,I.name AS index_name
						,STUFF (CONVERT (NVARCHAR (MAX), (caIIF.index_key)), 1, 2, N'''') AS index_key
						,ISNULL (STUFF (CONVERT (NVARCHAR (MAX), (caIIF.include_key)), 1, 2, N''''), N'''') AS include_key
						,' + @Filter_Definition_String + N' AS filter_definition
						,DDIPS.avg_fragmentation_in_percent
						,I.type_desc
						,I.is_primary_key
						,I.is_unique
						,ttSRC.[rows]
						,ROW_NUMBER () OVER
										(
											PARTITION BY
												I.name
											ORDER BY
												DDIPS.avg_fragmentation_in_percent DESC
										) AS row_number_id
					FROM
						sys.dm_db_index_physical_stats (' + CONVERT (NVARCHAR (11), @Database_ID) + N', NULL, NULL, NULL, N''LIMITED'') DDIPS
						INNER JOIN sys.objects O ON O.[object_id] = DDIPS.[object_id]
							AND O.[type] IN (''U'', ''V'')
							AND O.is_ms_shipped = 0
							AND NOT

								(
									SCHEMA_NAME (O.[schema_id]) = N''dbo''
									AND O.name = N''sysdiagrams''
									AND O.[type] = ''U''
								)

						INNER JOIN sys.indexes I ON I.[object_id] = DDIPS.[object_id]
							AND I.index_id = DDIPS.index_id
							AND I.is_disabled <> 1
							AND I.is_hypothetical <> 1
						INNER JOIN dbo.#temp_sssr_row_counts ttSRC ON ttSRC.[object_id] = DDIPS.[object_id]
							AND ttSRC.database_id = DB_ID ()
						CROSS APPLY

							(
								SELECT
									 (
										SELECT
											  N'', ''
											+ C.name AS [text()]
										FROM
											sys.index_columns IC
											INNER JOIN sys.columns C ON C.[object_id] = IC.[object_id]
												AND C.column_id = IC.column_id
										WHERE
											IC.is_included_column = 0
											AND IC.[object_id] = I.[object_id]
											AND IC.index_id = I.index_id
										ORDER BY
											IC.key_ordinal
										FOR
											 XML PATH ('''')
											,TYPE
									 ) AS index_key
									,(
										SELECT
											  N'', ''
											+ C.name AS [text()]
										FROM
											sys.index_columns IC
											INNER JOIN sys.columns C ON C.[object_id] = IC.[object_id]
												AND C.column_id = IC.column_id
										WHERE
											IC.is_included_column = 1
											AND IC.[object_id] = I.[object_id]
											AND IC.index_id = I.index_id
										ORDER BY
											IC.key_ordinal
										FOR
											 XML PATH ('''')
											,TYPE
									 ) AS include_key
							) caIIF

					WHERE
						DDIPS.index_id <> 0
						AND DDIPS.avg_fragmentation_in_percent > 5
				) sqIF

				CROSS APPLY

					(
						SELECT
							(CASE
								WHEN sqIF.avg_fragmentation_in_percent <= 30.0 THEN ''REORGANIZE''
								ELSE ''REBUILD''
								END) AS recommendation
					) caREC

			WHERE
				sqIF.row_number_id = 1
		 '


	EXECUTE (@SQL_String)


	SET @Database_Name_Loop =

		(
			SELECT TOP (1)
				X.database_name
			FROM
				dbo.#temp_sssr_databases X
			WHERE
				X.database_name <> N'tempdb'
				AND X.database_name > @Database_Name_Loop
			ORDER BY
				X.database_name
		)

END


IF NOT EXISTS (SELECT * FROM dbo.#temp_sssr_index_fragmentation X)
BEGIN

	GOTO Skip_Index_Fragmentation

END


IF @Output_Mode = 'E'
BEGIN

	SET @XML_String =

		CONVERT (NVARCHAR (MAX),
			(
				SELECT
					 '',X.object_type AS 'td'
					,'',X.database_name AS 'td'
					,'',X.[schema_name] AS 'td'
					,'',X.[object_name] AS 'td'
					,'',X.index_name AS 'td'
					,'',X.index_key AS 'td'
					,'',X.include_key AS 'td'
					,'',X.filter_definition AS 'td'
					,'','right_align' + X.fragmentation AS 'td'
					,'',X.index_type AS 'td'
					,'',X.is_pk AS 'td'
					,'',X.is_unique AS 'td'
					,'',X.recommendation AS 'td'
					,'','right_align' + X.row_count AS 'td'
					,'',X.alter_index_statement AS 'td'
				FROM
					dbo.#temp_sssr_index_fragmentation X
				ORDER BY
					 X.object_type
					,X.database_name
					,X.[schema_name]
					,X.[object_name]
					,X.index_name
				FOR
					XML PATH ('tr')
			)
		)


	SET @Body = @Body +

		N'
			<br>
			<h3><center>Index Fragmentation</center></h3>
			<center>
				<table frame=box border=1 cellpadding=2>
					<tr style="color: white; background-color: black">
						<th nowrap>Object Type</th>
						<th nowrap>Database Name</th>
						<th nowrap>Schema Name</th>
						<th nowrap>Object Name</th>
						<th nowrap>Index Name</th>
						<th nowrap>Index Key</th>
						<th nowrap>Include Key</th>
						<th nowrap>Filter Definition</th>
						<th nowrap>Fragmentation</th>
						<th nowrap>Index Type</th>
						<th nowrap>PK</th>
						<th nowrap>Unique</th>
						<th nowrap>Recommendation</th>
						<th nowrap>Row Count</th>
						<th nowrap>Alter Index Statement</th>
					</tr>
		 '


	SET @Body = @Body + @XML_String +

		N'
				</table>
			</center>
		 '

END
ELSE BEGIN

	SELECT
		 X.object_type
		,X.database_name
		,X.[schema_name]
		,X.[object_name]
		,X.index_name
		,X.index_key
		,X.include_key
		,X.filter_definition
		,X.fragmentation
		,X.index_type
		,X.is_pk AS pk
		,X.is_unique AS [unique]
		,X.recommendation
		,X.row_count
		,X.alter_index_statement
	FROM
		dbo.#temp_sssr_index_fragmentation X
	ORDER BY
		 X.object_type
		,X.database_name
		,X.[schema_name]
		,X.[object_name]
		,X.index_name

END


Skip_Index_Fragmentation:


IF OBJECT_ID (N'tempdb.dbo.#temp_sssr_index_fragmentation', N'U') IS NOT NULL
BEGIN

	DROP TABLE dbo.#temp_sssr_index_fragmentation

END


-----------------------------------------------------------------------------------------------------------------------------
--	Main Query IX: Missing Indexes
-----------------------------------------------------------------------------------------------------------------------------

IF @Missing_Indexes = 0
BEGIN

	GOTO Skip_Missing_Indexes

END


CREATE TABLE dbo.#temp_sssr_missing_indexes

	(
		 object_type CHAR (2)
		,database_name NVARCHAR (128)
		,[schema_name] SYSNAME
		,[object_name] SYSNAME
		,unique_compiles VARCHAR (23)
		,user_seeks VARCHAR (23)
		,user_scans VARCHAR (23)
		,avg_total_user_cost VARCHAR (23)
		,avg_user_impact VARCHAR (23)
		,overall_impact VARCHAR (23)
		,impact_rank VARCHAR (23)
		,index_key NVARCHAR (MAX)
		,include_key NVARCHAR (MAX)
		,table_column_count VARCHAR (23)
		,index_column_count VARCHAR (23)
		,include_column_count VARCHAR (23)
		,index_pct_of_columns VARCHAR (23)
		,include_pct_of_columns VARCHAR (23)
		,total_pct_of_columns VARCHAR (23)
		,row_count VARCHAR (23)
		,create_index_statement NVARCHAR (MAX)
	)


SET @Database_Name_Loop =

	(
		SELECT TOP (1)
			X.database_name
		FROM
			dbo.#temp_sssr_databases X
		WHERE
			X.database_name <> N'tempdb'
		ORDER BY
			X.database_name
	)


WHILE @Database_Name_Loop IS NOT NULL
BEGIN

	SET @SQL_String = @Avoid_Truncation +

		N'
			USE [' + @Database_Name_Loop + N']


			INSERT INTO dbo.#temp_sssr_missing_indexes

				(
					 object_type
					,database_name
					,[schema_name]
					,[object_name]
					,unique_compiles
					,user_seeks
					,user_scans
					,avg_total_user_cost
					,avg_user_impact
					,overall_impact
					,impact_rank
					,index_key
					,include_key
					,table_column_count
					,index_column_count
					,include_column_count
					,index_pct_of_columns
					,include_pct_of_columns
					,total_pct_of_columns
					,row_count
					,create_index_statement
				)


			SELECT
				 O.[type] AS object_type
				,DB_NAME () AS database_name
				,SCHEMA_NAME (O.[schema_id]) AS [schema_name]
				,O.name AS [object_name]
				,REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, DDMIGS.unique_compiles), 1)), 4, 23)) AS unique_compiles
				,REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, DDMIGS.user_seeks), 1)), 4, 23)) AS user_seeks
				,REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, DDMIGS.user_scans), 1)), 4, 23)) AS user_scans
				,CONVERT (VARCHAR (23), CONVERT (MONEY, DDMIGS.avg_total_user_cost), 1) AS avg_total_user_cost
				,CONVERT (VARCHAR (23), CONVERT (MONEY, DDMIGS.avg_user_impact), 1) AS avg_user_impact
				,CONVERT (VARCHAR (23), CONVERT (MONEY, caIC.overall_impact), 1) AS overall_impact
				,REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, DENSE_RANK () OVER
																										(
																											ORDER BY
																												caIC.overall_impact DESC
																										)), 1)), 4, 23)) AS impact_rank
				,ISNULL (DDMID.equality_columns + caIC.comma_or_not, N'''') + ISNULL (DDMID.inequality_columns, N'''') AS index_key
				,ISNULL (DDMID.included_columns, N'''') AS include_key
				,REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, sqCC.table_column_count), 1)), 4, 23)) AS table_column_count
				,REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, caIC.index_column_count), 1)), 4, 23)) AS index_column_count
				,REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, caIC.include_column_count), 1)), 4, 23)) AS include_column_count
				,CONVERT (VARCHAR (23), CONVERT (MONEY, (caIC.index_column_count / sqCC.table_column_count) * 100), 1) AS index_pct_of_columns
				,CONVERT (VARCHAR (23), CONVERT (MONEY, (caIC.include_column_count / sqCC.table_column_count) * 100), 1) AS include_pct_of_columns
				,CONVERT (VARCHAR (23), CONVERT (MONEY, ((caIC.index_column_count + caIC.include_column_count) / sqCC.table_column_count) * 100), 1) AS total_pct_of_columns
				,REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, ttSRC.[rows]), 1)), 4, 23)) AS row_count
				,caCIS.create_index_statement
			FROM
				sys.objects O
				INNER JOIN sys.dm_db_missing_index_details DDMID ON DDMID.[object_id] = O.[object_id]
					AND DDMID.database_id = DB_ID ()
				INNER JOIN sys.dm_db_missing_index_groups DDMIG ON DDMIG.index_handle = DDMID.index_handle
				INNER JOIN sys.dm_db_missing_index_group_stats DDMIGS ON DDMIGS.group_handle = DDMIG.index_group_handle
				INNER JOIN

					(
						SELECT
							 C.[object_id]
							,COUNT (*) + .0 AS table_column_count
						FROM
							sys.columns C
						GROUP BY
							C.[object_id]
					) sqCC ON sqCC.[object_id] = O.[object_id]

				INNER JOIN dbo.#temp_sssr_row_counts ttSRC ON ttSRC.[object_id] = O.[object_id]
					AND ttSRC.database_id = DB_ID ()
				CROSS APPLY

					(
						SELECT
							 CONVERT (DECIMAL (18, 2), (DDMIGS.user_seeks + DDMIGS.user_scans) * (DDMIGS.avg_total_user_cost * DDMIGS.avg_user_impact)) AS overall_impact
							,ISNULL ((LEN (DDMID.equality_columns) - LEN (REPLACE (DDMID.equality_columns, N'','', N''''))) + 1, 0) + ISNULL ((LEN (DDMID.inequality_columns) - LEN (REPLACE (DDMID.inequality_columns, N'','', N''''))) + 1, 0) AS index_column_count
							,ISNULL ((LEN (DDMID.included_columns) - LEN (REPLACE (DDMID.included_columns, N'','', N''''))) + 1, 0) AS include_column_count
							,(CASE
								WHEN DDMID.inequality_columns IS NOT NULL THEN N'', ''
								ELSE N''''
								END) AS comma_or_not
							,N''IX_''
							 + O.name
							 + N''_''
							 + ISNULL (REPLACE (SUBSTRING (SUBSTRING (DDMID.equality_columns, 1, LEN (DDMID.equality_columns) - 1), 2, LEN (DDMID.equality_columns) - 1), N''], ['', N''_'')
							 + (CASE
									WHEN DDMID.inequality_columns IS NOT NULL THEN N''_''
									ELSE N''''
									END), N'''')
							 + ISNULL (REPLACE (SUBSTRING (SUBSTRING (DDMID.inequality_columns, 1, LEN (DDMID.inequality_columns) - 1), 2, LEN (DDMID.inequality_columns) - 1), N''], ['', N''_''), N'''')
							 + ISNULL (N''_i_'' + REPLACE (SUBSTRING (SUBSTRING (DDMID.included_columns, 1, LEN (DDMID.included_columns) - 1), 2, LEN (DDMID.included_columns) - 1), N''], ['', N''_''), N'''') AS index_base_string
					) caIC

				CROSS APPLY

					(
						SELECT
							N''CREATE NONCLUSTERED INDEX ''
							+ (CASE
									WHEN LEN (caIC.index_base_string COLLATE DATABASE_DEFAULT) > 128 THEN N''<INDEX NAME>''
									ELSE N''['' + caIC.index_base_string + N'']''
									END)
							+ N'' ON ''
							+ N''[''
							+ DB_NAME ()
							+ N'']''
							+ N''.''
							+ N''[''
							+ SCHEMA_NAME (O.[schema_id])
							+ N'']''
							+ N''.''
							+ N''[''
							+ O.name
							+ N'']''
							+ N'' (''
							+ ISNULL (DDMID.equality_columns + caIC.comma_or_not, N'''')
							+ ISNULL (DDMID.inequality_columns, N'''')
							+ N'')''
							+ ISNULL (N'' INCLUDE ('' + DDMID.included_columns + N'')'', N'''')
							+ N'';'' AS create_index_statement
					) caCIS

			WHERE
				O.[type] IN (''U'', ''V'')
				AND O.is_ms_shipped = 0
				AND NOT

					(
						SCHEMA_NAME (O.[schema_id]) = N''dbo''
						AND O.name = N''sysdiagrams''
						AND O.[type] = ''U''
					)
		 '


	EXECUTE (@SQL_String)


	SET @Database_Name_Loop =

		(
			SELECT TOP (1)
				X.database_name
			FROM
				dbo.#temp_sssr_databases X
			WHERE
				X.database_name <> N'tempdb'
				AND X.database_name > @Database_Name_Loop
			ORDER BY
				X.database_name
		)

END


IF NOT EXISTS (SELECT * FROM dbo.#temp_sssr_missing_indexes X)
BEGIN

	GOTO Skip_Missing_Indexes

END


IF @Output_Mode = 'E'
BEGIN

	SET @XML_String =

		CONVERT (NVARCHAR (MAX),
			(
				SELECT
					 '',X.object_type AS 'td'
					,'',X.database_name AS 'td'
					,'',X.[schema_name] AS 'td'
					,'',X.[object_name] AS 'td'
					,'','right_align' + X.unique_compiles AS 'td'
					,'','right_align' + X.user_seeks AS 'td'
					,'','right_align' + X.user_scans AS 'td'
					,'','right_align' + X.avg_total_user_cost AS 'td'
					,'','right_align' + X.avg_user_impact AS 'td'
					,'','right_align' + X.overall_impact AS 'td'
					,'','right_align' + X.impact_rank AS 'td'
					,'',X.index_key AS 'td'
					,'',X.include_key AS 'td'
					,'','right_align' + X.table_column_count AS 'td'
					,'','right_align' + X.index_column_count AS 'td'
					,'','right_align' + X.include_column_count AS 'td'
					,'','right_align' + X.index_pct_of_columns AS 'td'
					,'','right_align' + X.include_pct_of_columns AS 'td'
					,'','right_align' + X.total_pct_of_columns AS 'td'
					,'','right_align' + X.row_count AS 'td'
					,'',X.create_index_statement AS 'td'
				FROM
					dbo.#temp_sssr_missing_indexes X
				ORDER BY
					 X.object_type
					,X.database_name
					,X.[schema_name]
					,X.[object_name]
					,X.impact_rank
					,X.index_key
					,X.include_key
				FOR
					XML PATH ('tr')
			)
		)


	SET @Body = @Body +

		N'
			<br>
			<h3><center>Missing Indexes</center></h3>
			<center>
				<table frame=box border=1 cellpadding=2>
					<tr style="color: white; background-color: black">
						<th nowrap>Object Type</th>
						<th nowrap>Database Name</th>
						<th nowrap>Schema Name</th>
						<th nowrap>Object Name</th>
						<th nowrap>Unique Compiles</th>
						<th nowrap>User Seeks</th>
						<th nowrap>User Scans</th>
						<th nowrap>Avg User Cost</th>
						<th nowrap>Avg User Impact</th>
						<th nowrap>Overall Impact</th>
						<th nowrap>Impact Rank</th>
						<th nowrap>Index Key</th>
						<th nowrap>Include Key</th>
						<th nowrap>Table Column Count</th>
						<th nowrap>Index Column Count</th>
						<th nowrap>Include Column Count</th>
						<th nowrap>Index % Of Columns</th>
						<th nowrap>Include % Of Columns</th>
						<th nowrap>Total % Of Columns</th>
						<th nowrap>Row Count</th>
						<th nowrap>Create Index Statement</th>
					</tr>
		 '


	SET @Body = @Body + @XML_String +

		N'
				</table>
			</center>
		 '

END
ELSE BEGIN

	SELECT
		 X.object_type
		,X.database_name
		,X.[schema_name]
		,X.[object_name]
		,X.unique_compiles
		,X.user_seeks
		,X.user_scans
		,X.avg_total_user_cost AS avg_user_cost
		,X.avg_user_impact
		,X.overall_impact
		,X.impact_rank
		,X.index_key
		,X.include_key
		,X.table_column_count
		,X.index_column_count
		,X.include_column_count
		,X.index_pct_of_columns
		,X.include_pct_of_columns
		,X.total_pct_of_columns
		,X.row_count
		,X.create_index_statement
	FROM
		dbo.#temp_sssr_missing_indexes X
	ORDER BY
		 X.object_type
		,X.database_name
		,X.[schema_name]
		,X.[object_name]
		,X.impact_rank
		,X.index_key
		,X.include_key

END


Skip_Missing_Indexes:


IF OBJECT_ID (N'tempdb.dbo.#temp_sssr_missing_indexes', N'U') IS NOT NULL
BEGIN

	DROP TABLE dbo.#temp_sssr_missing_indexes

END


-----------------------------------------------------------------------------------------------------------------------------
--	Main Query X: Unused Indexes
-----------------------------------------------------------------------------------------------------------------------------

IF @Unused_Indexes = 0
BEGIN

	GOTO Skip_Unused_Indexes

END


CREATE TABLE dbo.#temp_sssr_unused_indexes

	(
		 object_type CHAR (2)
		,database_name NVARCHAR (128)
		,[schema_name] SYSNAME
		,[object_name] SYSNAME
		,index_name SYSNAME
		,index_key NVARCHAR (MAX)
		,include_key NVARCHAR (MAX)
		,filter_definition NVARCHAR (MAX)
		,[disabled] VARCHAR (3)
		,hypothetical VARCHAR (3)
		,row_count VARCHAR (23)
		,drop_index_statement NVARCHAR (4000)
	)


SET @Database_Name_Loop =

	(
		SELECT TOP (1)
			X.database_name
		FROM
			dbo.#temp_sssr_databases X
		WHERE
			X.database_name <> N'tempdb'
		ORDER BY
			X.database_name
	)


WHILE @Database_Name_Loop IS NOT NULL
BEGIN

	SET @SQL_String = @Avoid_Truncation +

		N'
			USE [' + @Database_Name_Loop + N']


			INSERT INTO dbo.#temp_sssr_unused_indexes

				(
					 object_type
					,database_name
					,[schema_name]
					,[object_name]
					,index_name
					,index_key
					,include_key
					,filter_definition
					,[disabled]
					,hypothetical
					,row_count
					,drop_index_statement
				)

			SELECT
				 O.[type] AS object_type
				,DB_NAME () AS database_name
				,SCHEMA_NAME (O.schema_id) AS [schema_name]
				,O.name AS [object_name]
				,I.name AS index_name
				,STUFF (CONVERT (NVARCHAR (MAX), (caIIF.index_key)), 1, 2, N'''') AS index_key
				,ISNULL (STUFF (CONVERT (NVARCHAR (MAX), (caIIF.include_key)), 1, 2, N''''), N'''') AS include_key
				,' + @Filter_Definition_String + N' AS filter_definition
				,(CASE
					WHEN I.is_disabled = 1 THEN ''Yes''
					ELSE ''No''
					END) AS [disabled]
				,(CASE
					WHEN I.is_hypothetical = 1 THEN ''Yes''
					ELSE ''No''
					END) AS hypothetical
				,REVERSE (SUBSTRING (REVERSE (CONVERT (VARCHAR (23), CONVERT (MONEY, ttSRC.[rows]), 1)), 4, 23)) AS row_count
				,N''USE [''
				 + DB_NAME ()
				 + N'']; IF EXISTS (SELECT * FROM sys.indexes I WHERE I.[object_id] = ''
				 + CONVERT (VARCHAR (11), I.[object_id])
				 + N'' AND I.index_id = ''
				 + CONVERT (VARCHAR (11), I.index_id)
				 + N'') BEGIN DROP INDEX [''
				 + I.name
				 + N''] ON [''
				 + SCHEMA_NAME (O.schema_id)
				 + N''].[''
				 + O.name
				 + N''] END;'' AS drop_index_statement
			FROM
				sys.indexes I
				INNER JOIN sys.objects O ON O.[object_id] = I.[object_id]
					AND O.[type] IN (''U'', ''V'')
					AND O.is_ms_shipped = 0
					AND NOT

						(
							SCHEMA_NAME (O.[schema_id]) = N''dbo''
							AND O.name = N''sysdiagrams''
							AND O.[type] = ''U''
						)

				INNER JOIN dbo.#temp_sssr_row_counts ttSRC ON ttSRC.[object_id] = I.[object_id]
					AND ttSRC.database_id = DB_ID ()
				CROSS APPLY

					(
						SELECT
							 (
								SELECT
									  N'', ''
									+ C.name AS [text()]
								FROM
									sys.index_columns IC
									INNER JOIN sys.columns C ON C.[object_id] = IC.[object_id]
										AND C.column_id = IC.column_id
								WHERE
									IC.is_included_column = 0
									AND IC.[object_id] = I.[object_id]
									AND IC.index_id = I.index_id
								ORDER BY
									IC.key_ordinal
								FOR
									 XML PATH ('''')
									,TYPE
							 ) AS index_key
							,(
								SELECT
									  N'', ''
									+ C.name AS [text()]
								FROM
									sys.index_columns IC
									INNER JOIN sys.columns C ON C.[object_id] = IC.[object_id]
										AND C.column_id = IC.column_id
								WHERE
									IC.is_included_column = 1
									AND IC.[object_id] = I.[object_id]
									AND IC.index_id = I.index_id
								ORDER BY
									IC.key_ordinal
								FOR
									 XML PATH ('''')
									,TYPE
							 ) AS include_key
					) caIIF

			WHERE
				I.[type] = 2
				AND I.is_primary_key = 0
				AND I.is_unique = 0
				AND NOT EXISTS

					(
						SELECT
							*
						FROM
							sys.index_columns XIC
							INNER JOIN sys.foreign_key_columns FKC ON FKC.parent_object_id = XIC.[object_id]
								AND FKC.parent_column_id = XIC.column_id
						WHERE
							XIC.[object_id] = I.[object_id]
							AND XIC.index_id = I.index_id
					)

				AND NOT EXISTS

					(
						SELECT
							*
						FROM
							master.sys.dm_db_index_usage_stats DDIUS
						WHERE
							DDIUS.database_id = DB_ID ()
							AND DDIUS.[object_id] = I.[object_id]
							AND DDIUS.index_id = I.index_id
					)
		 '


	EXECUTE (@SQL_String)


	SET @Database_Name_Loop =

		(
			SELECT TOP (1)
				X.database_name
			FROM
				dbo.#temp_sssr_databases X
			WHERE
				X.database_name <> N'tempdb'
				AND X.database_name > @Database_Name_Loop
			ORDER BY
				X.database_name
		)

END


IF NOT EXISTS (SELECT * FROM dbo.#temp_sssr_unused_indexes X)
BEGIN

	GOTO Skip_Unused_Indexes

END


IF @Output_Mode = 'E'
BEGIN

	SET @XML_String =

		CONVERT (NVARCHAR (MAX),
			(
				SELECT
					 '',X.object_type AS 'td'
					,'',X.database_name AS 'td'
					,'',X.[schema_name] AS 'td'
					,'',X.[object_name] AS 'td'
					,'',X.index_name AS 'td'
					,'',X.index_key AS 'td'
					,'',X.include_key AS 'td'
					,'',X.filter_definition AS 'td'
					,'',X.[disabled] AS 'td'
					,'',X.hypothetical AS 'td'
					,'','right_align' + X.row_count AS 'td'
					,'',X.drop_index_statement AS 'td'
				FROM
					dbo.#temp_sssr_unused_indexes X
				ORDER BY
					 X.object_type
					,X.database_name
					,X.[schema_name]
					,X.[object_name]
					,X.index_name
				FOR
					XML PATH ('tr')
			)
		)


	SET @Body = @Body +

		N'
			<br>
			<h3><center>Unused Indexes</center></h3>
			<center>
				<table frame=box border=1 cellpadding=2>
					<tr style="color: white; background-color: black">
						<th nowrap>Object Type</th>
						<th nowrap>Database Name</th>
						<th nowrap>Schema Name</th>
						<th nowrap>Object Name</th>
						<th nowrap>Index Name</th>
						<th nowrap>Index Key</th>
						<th nowrap>Include Key</th>
						<th nowrap>Filter Definition</th>
						<th nowrap>Disabled</th>
						<th nowrap>Hypothetical</th>
						<th nowrap>Row Count</th>
						<th nowrap>Drop Index Statement</th>
					</tr>
		 '


	SET @Body = @Body + @XML_String +

		N'
				</table>
			</center>
		 '

END
ELSE BEGIN

	SELECT
		 X.object_type
		,X.database_name
		,X.[schema_name]
		,X.[object_name]
		,X.index_name
		,X.index_key
		,X.include_key
		,X.filter_definition
		,X.[disabled]
		,X.hypothetical
		,X.row_count
		,X.drop_index_statement
	FROM
		dbo.#temp_sssr_unused_indexes X
	ORDER BY
		 X.object_type
		,X.database_name
		,X.[schema_name]
		,X.[object_name]
		,X.index_name

END


Skip_Unused_Indexes:


IF OBJECT_ID (N'tempdb.dbo.#temp_sssr_unused_indexes', N'U') IS NOT NULL
BEGIN

	DROP TABLE dbo.#temp_sssr_unused_indexes

END


-----------------------------------------------------------------------------------------------------------------------------
--	Variable Update: Finalize "@Body" Variable Contents
-----------------------------------------------------------------------------------------------------------------------------

IF @Output_Mode = 'E'
BEGIN

	SET @Body =

		N'
			<html>
				<body>
				<style type="text/css">
					table {font-size: 8.0pt; font-family: Arial; text-align: left; white-space: nowrap; empty-cells: show}
					tbody tr:nth-child(odd) {background-color: #ccc;}
					tr {text-align: left;}
				</style>
		 '

		+ SUBSTRING (@Body, 10, LEN (@Body) - 10) +

		N'
				</body>
			</html>
		 '


	SET @Body = REPLACE (REPLACE (@Body, N'<td>', N'<td nowrap>'), N'<td nowrap>right_align', N'<td nowrap align="right">')

END


-----------------------------------------------------------------------------------------------------------------------------
--	sp_send_dbmail: Deliver Results / Notification To End User(s)
-----------------------------------------------------------------------------------------------------------------------------

IF @Output_Mode = 'E'
BEGIN

	EXECUTE msdb.dbo.sp_send_dbmail

		 @recipients = @Recipients
		,@copy_recipients = @Copy_Recipients
		,@subject = @Subject
		,@body = @Body
		,@body_format = 'HTML'

END


-----------------------------------------------------------------------------------------------------------------------------
--	Cleanup: Drop Any Remaining Temp Tables
-----------------------------------------------------------------------------------------------------------------------------

IF OBJECT_ID (N'tempdb.dbo.#temp_sssr_databases', N'U') IS NOT NULL
BEGIN

	DROP TABLE dbo.#temp_sssr_databases

END


IF OBJECT_ID (N'tempdb.dbo.#temp_sssr_row_counts', N'U') IS NOT NULL
BEGIN

	DROP TABLE dbo.#temp_sssr_row_counts

END
GO

Rate

4.83 (69)

You rated this post out of 5. Change rating

Share

Share

Rate

4.83 (69)

You rated this post out of 5. Change rating