Baselines can be any set of metrics that have been recorded to give you an understanding of what is the norm. Having a baseline gives you another piece to the puzzle when troubleshooting performance issues. It is the “base” that is used as a comparison to highlight areas of performance degradation.
We are all aware how important it is to baseline our SQL Servers right?! We all take regular baselines of our SQL Servers right?! For some reason I’m guessing there is quite a high percentage of people who were thinking wrong to at least one of the above statements. Why do I say that? Well in October 2012 Paul Randal carried out a survey on baselines and posted the results here. I was surprised to see not only that more people did not have baselines than did, but also the reasoning for not having a baseline. In my opinion there is no excuse for not taking baselines, after all it doesn’t take much time or effort to create a baseline to capture only the most significant SQL Server metrics. My stance on this is firm but fair; if you’re a DBA and you are not taking baselines then you aren’t doing your job to the best of your abilities! Still don’t agree? Erin Stellato did a fantastic article over at SQL Server Central on Capturing Baselines on Production SQL Servers. With this you don’t even need to write anything, it is a simple copy and paste job with some familiarisation of the routine. Go on, take a look and give it a go and I’m sure you will see the value when the gremlins get into your environment and you’re frantically trying to find the problem.
Understanding how to measure performance allows you to analyse the impact on your environments the decisions you've made have had, while also assisting you with future decisions. Too many times I have seen decisions being made incorrectly, myself included, because of a lack of information. Without a full picture it is impossible to be sure the decision you are about to make is correct. Basically what I am getting at is the more information about your environments you have at your disposal the better the chance of you making the right decisions. It is worth mentioning at this point I don’t mean go and collect every single performance counter in perfmon and or all the meta-data information in the DMV’s. The key is to find the right metrics for your environments.
The main area that most people measure is the performance of the SQL Server itself, including both Windows (perfmon) and SQL Server (DMV’s). While this information is invaluable to have not many people measure the performance of the databases, specifically the associated code within SQL Server and the applications that abuse it. In this post I am going to provide one way which I have used over the years to give me a representation of the performance of the statements being run against a server.
Below are just a few things over the years I have used baselines for;
• Give me a representation of “normal” performance
• Troubleshoot performance issues
• Capacity Planning
• Monitoring the Impact of an application release
Recently however I used a baseline for something completely different; to show some of the benefits that SQL Sentry Performance Advisor would give us as a DBA team in our day to day roles. For those of you not familiar with SQL Sentry, the products they offer, in particular Performance Advisor I suggest you head over to their website here. In summary;
“Performance Advisor for SQL Server provides unparalleled insight, awareness and control over the true source of SQL Server performance issues. Performance Advisor is packed with ground-breaking features that aren’t found in any other performance monitoring software, all designed with the singular goal of simplifying the process of SQL Server performance tuning and optimization”.
Performance Advisor comprises of a Monitor Service which monitors one or more SQL Server Instances a Repository Database which requires SQL Server and a client application for viewing the collected trace data.
For the purposes of this post I will protect sensitive information by replacing names etc. The environment I have configured is as below;
ServerA
This Server runs the SQL Sentry Performance Advisor Monitor Service and SQL Sentry repository database. This server also runs an instance of Reporting Services that a small reporting solution is deployed too.
ServerB
This is a production SQL Server that is monitored by SQL Sentry Performance Advisor.
To configure this environment you will also need a minimum of two servers as above. Below is a breakdown of the process.
Configure ServerA
Install SQL Sentry Performance Advisor and monitor at least one instance of SQL Server.
Create the baseline routine
While Performance Advisor is “doing its thang” create the routine to aggregate the trace data and produce our baseline.
Take the initial baseline
This is simple leave Performance Advisor to run for 7 days monitoring ServerB, don’t react to or change anything that is not business critical during this time to ensure results are as accurate as possible.
Analyse the initial baseline
The initial baseline will be based on 7 days of trace data, analyse this data based on the baseline routine created.
Tune, Tune, Tune
Spend the next 7 days performance tuning as you see fit based on the picture that Performance Advisor has painted.
Take the second baseline
Once you are happy with the amount of tuning you have done take the second baseline. My advice here is that you align the initial and second baselines to start and end on the same weekday and time. I start on Monday at 00:00 and end on Sunday at 23:59:59.
Compare the initial and second baselines
Compare the results of the initial and second baselines.
NOTE – This routine has been developed and deployed to SQL Server 2008 R2 and has not been tested on any other version. Although every care has been taken to ensure it works with SQL Server 2008, 2008R2 and 2012, I cannot guarantee it.
The routine can be found here to download and implement.
Configure ServerA
As I mentioned earlier this Server will run the Monitor Service and also house the SQL Sentry repository database. The SQL Sentry Quick Start Guide will detail how to install and configure Performance Advisor. The trial is for 15 days however this baseline exercise may last for 21-28 days depending on the length of the tuning process. Alternatively you could reduce the period that the initial and second baselines are captured for so the entire process fits within the 15 days. The reason I used 7 days is that there are processes that run over the weekend which I wanted to capture, tune and compare. The SQL Sentry team are very good, they offer some of the best vendor support I have ever received, I would suggest speaking to them if you need to extend the trial beyond the 15 days. Once Performance Advisor is installed and configured on ServerA to monitor ServerB you are ready to move onto the next step.
There is a ReadMe.txt in the DatabaseScripts folder that details a few requirements, but the scripts are pretty straight forward to follow.
The routine aggregates data and records the SpeedIndex (this is the metric used throughout the routine) for different intervals. One of the intervals Business Day is defined in the routine as 08:00 to 22:00, this is specific to my current employer and if this does not reflect your business day you will need to update this in 3 of the scripts and save the files before running them.
Update the 2 - CreateDatabaseObjects.sql script;
There are three instances of 08:00 and 22:00 in this script.
Update the 3 - InsertLookupTableData.sql script;
There is one instance of 08:00 and 22:00 in this script.
Update the 4 - CreateJobs.sql script;
There are two instances of 08:00 and 22:00 in this script
There is also a stored procedure used by the Reporting Solution in 2 - CreateDatabaseObjects.sql script that uses REPLACE to remove the trailing domain from the server name. This will need updating to your domain if you also want to remove the trailing domain for neatness. There are two instances of .YourDomain.Here in 2 - CreateDatabaseObjects.sql that you will need update accordingly. For example if your domain was sql365.local then you would replace .YourDomain.Here with .sql365.local
Create the baseline routine
To create the baseline routine you will need to run 4 scripts in the DatabaseScripts folder, in order, which I have listed below;
1 - CreateITInternalDatabase.sql
2 - CreateDatabaseObjects.sql
3 - InsertLookupTableData.sql
4 - CreateJobs.sql
A list of the objects created by the scripts is below;
1 x Database
ITInternal
3 x Schemas
Archive
Kpi
Reports
6 x Tables
Archive.tblSpeedIndexByMinute
Archive.tblSpeedIndexSummary
kpi.tblLookupServerName
kpi.tblLookupSpeedIndexDescription
kpi.tblSpeedIndexByMinute
kpi.tblSpeedIndexSummary
16 x Stored Procedures
Archive.spArchivetblSpeedIndexByMinute
Archive.spArchivetblSpeedIndexSummary
kpi.spGetInfoForInsertSpeedIndexByMinute
kpi.spInsertSpeedIndexByMinute
kpi.spInsertSpeedIndexSummary
kpi.spInsertSpeedIndexSummaryMoreThanOneDay
kpi.spShowPerformanceSpeedIndex
kpi.spShowPerformanceSpeedIndexMoreThan1Day
Reports.spGetAverageSpeedIndexForCurrentMonth
Reports.spGetMaxSpeedIndexSummaryCurrentBusinessDay
Reports.spGetServerName
Reports.spGetSpeedIndexByDay
Reports.spGetSpeedIndexByMinuteCurrentBusinessDay
Reports.spGetSpeedIndexDescription
Reports.spGetSpeedIndexSummary
Reports.spGetSpeedIndexSummaryYesterday
8 x Jobs
(00:30) - Insert Speed Index For Last 24 Hours
(22:30) - Insert Speed Index For Last Business Day
(Every 5 Minutes) - Insert Speed Index For Last 5 Minutes
(Every Hour) - Insert Speed Index For Last Hour
(Every Minute) - Insert Speed Index For The Minute
(Mon 01:00) - Insert Speed Index For Last Week
(Monthly) - ArchiveSpeedIndexByMinuteData
(Monthly) - Insert Speed Index For Last Month
Once the scripts have been run, you can then deploy the Reporting Services Solution. The solution is in the ReportingServicesSolution folder and comprises of;
2 x Data Sources
ITInternal
SQLSentry
2 x Shared Data sets
ServerName
SpeedIndexDescription
3 x Reports
SpeedIndexByDay
This shows a 30 day representation of the SpeedIndex for a given server.
SpeedIndexCurrentBusinessDay
This shows a one day representation of the SpeedIndex for the current day along with the SpeedIndex for;
Max 5 Min Period
Max 1 Hour Period
Last 24 Hours
SpeedIndexForBusinessDay – Email
This is the same as SpeedIndexCurrentBusinessDay but has an added date parameter, I use this for historical checks and also for subscriptions hence the Email in the title.
Both Data Sources need to be set to use the server that you configured earlier (ServerA);
Double click each Data Source and at the Shared Data Source Properties window (see below) click Edit.
At the Connection properties window (see below) type the server name you have used as ServerA and click OK.
The only other thing to change is to update the Deployment Settings to point to the Report Server instance and path where you want to deploy the reports and of course deploy the solution.
To do this right click DatabaseTeamKPI top level in solution explorer (see below) and select properties;
The TargetServerURL needs to be updated to reflect the server you want to house the reports. If this is different to ServerA then the servernamehere will need to reflect this. Once amended, simply right click DatabaseTeamKPI again and select Build, then right click DatabaseTeamKPI one last time and click deploy.
The routine and reports are now setup; it is time to take the initial baseline.
Take the initial baseline
Not too much to do here, the SQL Agent jobs created by the database scripts will aggregate the trace data collected by Performance Advisor and create the baseline. You can view the data collected by querying the tables or using the reports in the reporting solution, to familiarise yourself with the routine while the initial baseline is being collected so you can hit the ground running with the analysis.
What I suggest here is that you export the SpeedIndexByDay report once a day for use when comparing the two baselines. This is not an essential as the data the reports use is still record so can be used historically.
Analyse the initial baseline
The SpeedIndex is the figure we are using here, the lower the better, however each environment will be different so you will have to use your own judgement as to what is acceptable in your environment. I’ve included a summary of the how the SpeedIndex is calculated below;
SpeedIndex = ((QueryHitCount * QueryAverageDuration / 1000000) * 420 / Duration of Aggregation)
Tune, Tune, Tune
The SQLSentryPerformance.sql file contains two queries that both return the SpeedIndex and the most expensive queries run against ServerB. The first will be run against data between two dates the second will be run against data between two times on the same day. The second result set that contains the queries will give execution duration and read / write data. I would take the top 25 – 50 queries and tune them. Now when I say tune this can vary dramatically from removing an order by or adding an index, to completely refactoring a query. SQL Sentry Plan Explorer that ships with Performance Advisor is invaluable here to help identify expensive operators in the execution plans.
Take the second baseline
Not too much to do here, the SQL Agent jobs created by the database scripts will aggregate the trace data collected by Performance Advisor and create the baseline.
NOTE – Do not make any changes during this period that are not business critical. This will help improve the accuracy of the baselines and the impact of the tuning.
What I suggest here is that you export the SpeedIndexByDay report once a day for use when comparing the two baselines. This is not an essential as the data the reports use is still record so can be used historically. Another piece of advice that I mentioned earlier is that you align the initial and second baselines to start and end on the same weekday and time. I start on Monday at 00:00 and end on Sunday at 23:59:59.
At my current employer we were able to achieve a 40% – 50% reduction in the SpeedIndex KPI during one week of tuning.
Compare the initial and second baselines
Another simple task here, compare the SpeedIndex values of the initial and second baselines. How in depth you want to go here is entirely up to you. The lowest level of granularity for the SpeedIndex that the routine captures is 1 minute.
This routine has proved extremely valuable and the impact visible not only to the DBA team but to the IT management team. So much so that off the back of this and a few other bells and whistles that Performance Advisor provides, we were approved to purchase the required licenses of Performance Advisor to monitor our core SQL Server clusters!
I hope this post gives you an insight into the importance of baselines and the role they play in the day to day monitoring of your SQL Servers. I also hope that the routine allows you to gain in my eyes “another valuable metric” and a means by which to “Sell” SQL Sentry Performance Advisor to your management team as a truly fantastic tool which gives you so much more than just the data that the routine uses.
As always, Enjoy!
Chris
Chris McGowan's SQL Server perspicuity, for all things SQL related and some, no doubt, not!
Monday, 24 June 2013
Thursday, 23 May 2013
Dedicated Administrator Connection (DAC)
We all dread the scenario whereby SQL Server is under so much load and has a complete lack of resources that no further connections can be made. Although extreme and a situation I have never been in there is a saving grace, one last chance before opting for measures that will induce outages, the Dedicated Administrator Connection or DAC.
First introduced in SQL Server 2005 the DAC is a special connection that is accessible when all other connections fail. The DAC is available via both SQLCMD and SSMS however it is recommended to use SQLCMD as it uses fewer resources than the GUI of SSMS. The sole purpose of the DAC is for diagnosing problems when no other connection can be made, it is not to be used as a regular connection. It is also recommended that you connect to the master database when using the DAC and that you do not run any resource intensive queries.
There are some limitations which I will detail below;
1 - Only one connection to the DAC is allowed, if already in use any further connections will be rejected
The below query it will return the SPID for the DAC if it is in use;
3 - Only users with membership in the sysadmin fixed server role can connect to the DAC
4 - Some SQL Statements are unavailable using the DAC, for example BACKUP or RESTORE.
Below are some examples of connecting to the DAC using both SQLCMD and SSMS, If you have never connected to the DAC or used SQLCMD for that matter, I recommend testing connectivity to the DAC and becoming familiar with SQLCMD. This will save precious time in the event of a serious problem, you really don't want to be googling or boling when facing a potential outage.
Example 1: Connect to the DAC using SQLCMD and integrated security
Open command prompt and run;
sqlcmd -S ServerNameHere\InstanceNameHere -d master -A
Example 2: Connect to the DAC using SQLCMD and SQL authentication
Open command prompt and run;
sqlcmd -S ServerNameHere\InstanceNameHere -U UserNameHere -P PasswordHere -d master -A
Example 3: Connect to the DAC using SSMS
Open SSMS, at the connection window prefix the ServerName or ServerName\InstanceName with ADMIN:
ADMIN:SQL365
or
ADMIN:SQL365\INST01
Enjoy!
Chris
First introduced in SQL Server 2005 the DAC is a special connection that is accessible when all other connections fail. The DAC is available via both SQLCMD and SSMS however it is recommended to use SQLCMD as it uses fewer resources than the GUI of SSMS. The sole purpose of the DAC is for diagnosing problems when no other connection can be made, it is not to be used as a regular connection. It is also recommended that you connect to the master database when using the DAC and that you do not run any resource intensive queries.
There are some limitations which I will detail below;
1 - Only one connection to the DAC is allowed, if already in use any further connections will be rejected
The below query it will return the SPID for the DAC if it is in use;
SELECT s.session_id FROM sys.tcp_endpoints AS E INNER JOIN sys.dm_exec_sessions AS S ON E.endpoint_id = S.endpoint_id WHERE E.name = 'Dedicated Admin Connection';2 - By default the DAC is only available locally, this can be changed by enabling remote admin connections using sp_configure
3 - Only users with membership in the sysadmin fixed server role can connect to the DAC
4 - Some SQL Statements are unavailable using the DAC, for example BACKUP or RESTORE.
Below are some examples of connecting to the DAC using both SQLCMD and SSMS, If you have never connected to the DAC or used SQLCMD for that matter, I recommend testing connectivity to the DAC and becoming familiar with SQLCMD. This will save precious time in the event of a serious problem, you really don't want to be googling or boling when facing a potential outage.
Example 1: Connect to the DAC using SQLCMD and integrated security
Open command prompt and run;
sqlcmd -S ServerNameHere\InstanceNameHere -d master -A
Example 2: Connect to the DAC using SQLCMD and SQL authentication
Open command prompt and run;
sqlcmd -S ServerNameHere\InstanceNameHere -U UserNameHere -P PasswordHere -d master -A
Example 3: Connect to the DAC using SSMS
Open SSMS, at the connection window prefix the ServerName or ServerName\InstanceName with ADMIN:
ADMIN:SQL365
or
ADMIN:SQL365\INST01
Enjoy!
Chris
Wednesday, 15 May 2013
Object Qualification
Now for those of you that don’t know SQL Server has to do an awfull lot of
work before a statement is actually executed, here I want to show you the
performance improvements that can be achieved by schema qualifying your
objects. The below quote is from
Microsoft and will set the scene for the rest of the post.
"If user "dbo" owns object
dbo.mystoredproc, and another user "Harry" runs this stored procedure
with the command "exec mystoredproc," the initial cache lookup by
object name fails because the object is not owner-qualified. (It is not yet
known whether another stored procedure named Harry.mystoredproc exists, so SQL
cannot be sure that the cached plan for dbo.mystoredproc is the right one to
execute.) SQL Server then acquires an exclusive compile lock on the procedure
and makes preparations to compile the procedure, including resolving the object
name to an object ID. Before it compiles the plan, SQL Server uses this object
ID to perform a more precise search of the procedure cache and is able to
locate a previously compiled plan even without the owner qualification.
If an existing plan is found, SQL Server
reuses the cached plan and does not actually compile the stored procedure.
However, the lack of owner-qualification forces SQL to perform a second cache
lookup and acquire an exclusive compile lock before determining that the
existing cached execution plan can be reused. Acquiring the lock and performing
lookups and other work that is needed to get to this point can introduce a
delay that is sufficient for the compile locks to lead to blocking. This is
especially true if a large number of users who are not the stored procedure's
owner simultaneously run it without supplying the owner name. Note that even if
you do not see SPIDs waiting on compile locks, lack of owner-qualification can
introduce delays in stored procedure execution and unnecessarily high CPU
utilization."
Script
To demonstrate this I used the below script, I am running SQL Server 2008
R2 developer edition on my local instance and used the AdventureWorks2008R2
databasewhich is available here.
The script creates a schema called Chris in the AdventureWorks2008R2
database, a user SQL365\Chris is created for the login SQL365\Chris with the
default schema of Chris. Finally a
procedure called dbo.spGetSalesOrderHeader is created that returns every record
from AdventureWorks2008R2.dbo.SalesOrderHeader.
/*
-----------------------------------------------------------------
Object Qualification
-----------------------------------------------------------------
For more SQL resources, check out
SQLServer365.blogspot.com
-----------------------------------------------------------------
You may alter this code for your own
purposes.
You may republish altered code as long as
you give due credit.
You must obtain prior permission before
blogging this code.
THIS CODE AND INFORMATION ARE PROVIDED
"AS IS"
-----------------------------------------------------------------
*/
-- Set Database
Context
USE
AdventureWorks2008R2;
GO
-- Declare
variable
DECLARE @SQL VARCHAR(255)
-- Set variable
SET @SQL = 'CREATE SCHEMA Chris
AUTHORIZATION dbo'
-- Create Schema
IF NOT EXISTS ( SELECT 1
FROM sys.schemas
WHERE name = 'Chris' )
BEGIN
EXEC (@SQL)
END
GO
-- Create user
mapped to login with default schema of the above created schema
IF NOT EXISTS ( SELECT 1
FROM sys.database_principals
WHERE name = 'SQL365\chris' )
BEGIN
CREATE USER [SQL365\Chris] FOR
LOGIN [SQL365\Chris] WITH
DEFAULT_SCHEMA =
Chris;
END
GO
-- Create
procedure in dbo schema to be executed by the above user
IF EXISTS ( SELECT 1
FROM sys.objects
WHERE [object_id] =
OBJECT_ID(N'[dbo].[spGetSalesOrderHeader]')
AND
type IN ( N'P', N'PC' ) )
DROP PROCEDURE [dbo].[spGetSalesOrderHeader]
GO
CREATE PROCEDURE dbo.spGetSalesOrderHeader
AS
BEGIN
SELECT SalesOrderID ,
RevisionNumber ,
OrderDate ,
DueDate ,
ShipDate ,
[Status] ,
OnlineOrderFlag ,
SalesOrderNumber ,
PurchaseOrderNumber ,
AccountNumber ,
CustomerID ,
SalesPersonID ,
TerritoryID ,
BillToAddressID ,
ShipToAddressID ,
ShipMethodID ,
CreditCardID ,
CreditCardApprovalCode ,
CurrencyRateID ,
SubTotal ,
TaxAmt ,
Freight ,
TotalDue ,
Comment ,
rowguid ,
ModifiedDate
FROM sales.SalesOrderHeader
END
GO
I use a great tool SQLQueryStress developed by Adam Machanic (B - T) quite frequently when
testing the effects of changes under load, it is ingeniously simple to use and
I love it. I used SQLQueryStress to
record the results of executing the procedure and without schema qualification and
with schema qualification, I used 4 threads (the number of cores in my laptop)
and ran a thousand iterations to get a good average. Results of which are included below;
Non Schema Qualified

Schema Qualified
As you can see the results are pretty damn conclusive every
metric measured by SQLQueryStress saw a performance improvement by schema
qualifying objects.
Run Time - 27.01%
Improvement
ClientSeconds/Iteration (Avg) - 11.99% Improvement
CPU Seconds/Iteration (Avg) - 1.13% Improvement
Actual Seconds/Iteration (Avg) - 12.54% Improvement
There is no excuse for not schema qualifying your objects,
performance improvements like this just cannot be ignored.
Enjoy!
Chris
Thursday, 9 May 2013
DTA and Hypothetical Indexes
For those of you that don’t know DTA stands for Database
Engine Tuning Adviser and is available from the Tools menu in Management
Studio. This tool was first introduced
in SQL Server 2005 and has been a much used tool by DBA’s and Developers alike
in most of the companies I have worked for.
There are two main problems I have with DTA firstly the
default object name prefixes are terrible, no really I mean absolutely
awful. See the table below for examples.
Object type
|
Default object name prefixes
|
Example
|
Indexes
|
_dta_index_
|
_dta_index_dta_mv_1_7_1150627142_K2
|
Statistics
|
_dta_stat_
|
_dta_stat_2041058307_2_5
|
Views
|
_dta_mv_
|
_dta_mv_3
|
Partition functions
|
_dta_pf_
|
_dta_pf_1043
|
Partition schemes
|
_dta_ps_
|
_dta_ps_1040
|
Now there is no right or wrong way to standardise the
names of your database objects but indexes for example I go with the below;
Single Index Key Column - IDX_TableName:ColumnName
Multi Index Key Column – IDX_TableName:CompositeX
Some people will agree some won’t, but in my experience
this makes life easier for me and the team when maintaining our SQL estate.
The other problem is that while DTA is analysing a
workload, it automatically creates the recommended indexes with the meaningless
names as mentioned above. DTA will
always clean up the indexes it creates; well actually that is a lie. If the DTA process exits then the indexes it
has created so far will persist!
We can identify these indexes by the value of the
is_hypothetical column of the sys.indexes catalog view, this will be = 1.
I have created the below script which will email a list
of hypothetical indexes and the script to drop them, simply schedule this as a
SQL Server Agent job as you see fit changing the @EmailProfile and
@EmailRecipient variables accordingly;
/*
-----------------------------------------------------------------
Hypothetical Indexes
-----------------------------------------------------------------
For more SQL resources, check out
SQLServer365.blogspot.com
-----------------------------------------------------------------
You may alter this code for your own
purposes.
You may republish altered code as long as
you give due credit.
You must obtain prior permission before
blogging this code.
THIS CODE AND INFORMATION ARE PROVIDED
"AS IS"
-----------------------------------------------------------------
*/
-- Set database
context
USE master;
GO
-- Declare
variables
DECLARE
@EmailProfile VARCHAR(255)
DECLARE
@EmailRecipient VARCHAR(255)
DECLARE
@EmailSubject VARCHAR(255)
-- Set variables
SET
@EmailProfile = 'DBA'
SET
@EmailRecipient = 'Chris@SQLServer365.com'
SET
@EmailSubject = 'ALERT
- Hypothetical Indexes found on ' + @@SERVERNAME
-- Drop
temporary table if exists
IF OBJECT_ID('tempDB.dbo.#HypotheticalIndexDropScript') IS NOT NULL
DROP TABLE #HypotheticalIndexDropScript;
-- Create
Temporary Table
CREATE TABLE #HypotheticalIndexDropScript
(
DatabaseName VARCHAR(255) ,
HypotheticalIndexDropScript VARCHAR(4000)
);
INSERT INTO
#HypotheticalIndexDropScript
EXEC sp_msforeachdb 'USE [?]; SELECT
DB_NAME(DB_ID()), ''USE '' + ''['' + DB_NAME(DB_ID()) + ''];'' + ''
IF EXISTS (SELECT 1 FROM
sys.indexes AS i WHERE i.[object_id] =
'' + ''object_id('' + + '''''''' + ''['' + SCHEMA_NAME(o.[schema_id]) + ''].''
+ ''['' + OBJECT_NAME(i.[object_id]) +
'']'' + '''''''' + '')'' + '' AND name = '' + '''''''' + i.NAME + '''''''' +
'') ''
+ '' DROP INDEX '' + ''['' + i.name +
'']'' + '' ON '' + ''['' + SCHEMA_NAME(o.[schema_id]) + ''].'' + ''['' +
OBJECT_NAME(o.[object_id]) + ''];'' AS HypotheticalIndexDropScript
FROM sys.indexes i
INNER JOIN sys.objects o ON
o.[object_id] = i.[object_id]
WHERE
is_hypothetical = 1'
-- Check for
hypothetical indexes
IF EXISTS ( SELECT 1
FROM #HypotheticalIndexDropScript )
BEGIN
DECLARE
@tableHTML NVARCHAR(MAX);
SET
@tableHTML = N'<style
type="text/css">'
+ N'.h1 {font-family: Arial,
verdana;font-size:16px;border:0px;background-color:white;} '
+ N'.h2 {font-family: Arial,
verdana;font-size:12px;border:0px;background-color:white;} '
+ N'body {font-family: Arial, verdana;} '
+ N'table{font-size:12px; border-collapse:collapse;border:1px
solid black; padding:3px;} '
+ N'td{background-color:#F1F1F1; border:1px solid black;
padding:3px;} '
+ N'th{background-color:#99CCFF; border:1px solid black;
padding:3px;}'
+ N'</style>' + N'<table border="1">' + N'<tr>'
+ N'<th>DatabaseName</th>'
+ N'<th>HypotheticalIndexDropScript</th>' + N'</tr>'
+ CAST(( SELECT td =
DatabaseName ,
'' ,
td = HypotheticalIndexDropScript ,
''
FROM #HypotheticalIndexDropScript
FOR
XML
PATH('tr') ,
TYPE
) AS NVARCHAR(MAX)) + N'</table>';
-- Email
results
EXEC
msdb.dbo.sp_send_dbmail @profile_name
= @EmailProfile,
@recipients = @EmailRecipient, @subject = @EmailSubject,
@body =
@tableHTML, @body_format = 'HTML';
END
GO
DTA is not coming out to play as he has been (and will
continue to be) a very naughty boy.
Enjoy!
Chris
Subscribe to:
Posts (Atom)




