<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>nlsimmons.com</title>
	<atom:link href="http://nlsimmons.com/?feed=rss2" rel="self" type="application/rss+xml" />
	<link>http://nlsimmons.com</link>
	<description>Professional Blog &#38; Online Resume</description>
	<lastBuildDate>Wed, 10 Jun 2015 15:02:01 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=4.2.2</generator>
	<item>
		<title>Configure SSL for SQL Server Reporting Services 2012</title>
		<link>http://nlsimmons.com/?p=259</link>
		<comments>http://nlsimmons.com/?p=259#comments</comments>
		<pubDate>Fri, 09 Aug 2013 21:16:46 +0000</pubDate>
		<dc:creator><![CDATA[]]></dc:creator>
				<category><![CDATA[Reporting Services]]></category>
		<category><![CDATA[Certificates]]></category>
		<category><![CDATA[SSL]]></category>
		<category><![CDATA[SSRS]]></category>

		<guid isPermaLink="false">http://nlsimmons.com/SQLScraps/?p=259</guid>
		<description><![CDATA[In this brief post I will show you how I created a self-signed certificate to be used when configuring SSRS&#8230;]]></description>
				<content:encoded><![CDATA[<p>In this brief post I will show you how I created a self-signed certificate to be used when configuring SSRS 2012 over SSL port 443. <span id="more-259"></span></p>
<h3>Procedure</h3>
<p>First you&#8217;re going to need to get <b>makecert.exe</b> and <b>pvk2pfx.exe</b>. Both of these utilities can be downloaded from Microsoft as part of the Windows Driver Kit. I put these utilities on the server on which I was configuring Reporting Services.</p>
<p>Before beginning, you can use the following command (cmd window) to see if there are any SSL Certificate bindings already present:</p>
<pre>netsh http show sslcert</pre>
<p><br/><br />
If there aren&#8217;t any, you should see output like this:<br />
<img src="http://nlsimmons.com/SQLScraps/wp-content/uploads/2013/08/netsh_initial.png" alt="netsh_initial" width="676" height="339" class="alignnone size-full wp-image-1059" align="center"/></p>
<p>So, on the the Reporting Services server:</p>
<p> 1. Open a command prompt, being sure to run as Administrator<br />
 2. Run makecert.exe as admin, specifying the certificate and private key filenames.  </p>
<pre>makecert -r -pe -n "CN=<SSRS Server>" -eku 1.3.6.1.5.5.7.3.1 -sr localmachine -sky exchange -sp "Microsoft RSA SChannel Cryptographic Provider" -sy 12 -sv "<Private Key filename>.pvk"  "<Certificate filename>.cer"</pre>
<p><br/><br />
You will need to specify a password for the Private Key:<br/><br />
<img src="http://nlsimmons.com/SQLScraps/wp-content/uploads/2013/08/pk_password.png" alt="pk_password" width="354" height="245" class="alignnone size-full wp-image-1060" align="center" /><br />
 3. Create a PFX file using pvk2pfx.exe, again in the cmd window running as Administrator:</p>
<pre>
"pvk2pfx.exe" -pvk "&lt;Private Key filename&gt;.pvk" -spc "&lt;Certificate filename&gt;.cer" -pfx "&lt;.pfx filename&gt;.pfx" -po "&lt;password used in the above step&gt;"
</pre>
<p><br/><br />
 4. Run MMC from the Start Menu -&gt; Run<br />
 5. Go to File -&gt; Add/Remove Snap-in<br />
 6. Choose &#8220;Computer Account&#8221; -&gt; Next<br />
 7. Make sure &#8220;Local Computer: (the computer this console is running on)&#8221; is checked<br />
 8. Expand Trusted Root Certification Authorities on left<br />
 9. Right click Certificates -&gt; All Tasks -&gt; Import<br />
10. Select PFX file created above<br />
11. Enter password and check &#8220;mark this key as exportable.&#8221;<br />
12. Be sure cert is going into the certificate store &#8220;Trusted Root Certification Authorities&#8221;<br />
13. Repeat steps 8-12, using the &#8220;Personal&#8221; folder instead of &#8220;Trusted Root Certification Authorities&#8221;<br />
14. Open up Reporting Services Configuration Manager and connect to the instance for which the SSL cert will be used<br />
15. Navigate to the Web Service URL portion of the manager<br />
16. Click on Advanced<br />
17. Remove HTTP identities<br />
18. Add the SSL Identity by clicking Add and selecting the certificate imported earlier<br />
19. Choose the correct SSL port and IP Addresses and click Ok<br />
20. Do steps 16-19 in the Report Manager URL</p>
<p>That&#8217;s it!  Now you should be able to run netsh again and get output similar to this:<br />
<img src="http://nlsimmons.com/SQLScraps/wp-content/uploads/2013/08/netsh_complete.png" alt="netsh_complete" width="676" height="340" class="alignnone size-full wp-image-1058" align="center"/></p>
<h3>Problems, Pitfalls, and Errors</h3>
<p>I struggled with setting this up, no thanks to some of the errors I was getting. Here&#8217;s one of my favorites, thrown during the CreateSSLCertificateBinding method: &#8220;An unknown error has occurred in the WMI Provider. Error Code 80070520 &#8212;&gt; System.Runtime.InteropServices.COMException (0x80070520): A specified logon session does not exist. It may already have been terminated. (Exception from HRESULT: 0x80070520)&#8221;</p>
<p><img alt="An unknown error has occurred in the WMI Provider. Error Code 80070520 ---&gt; System.Runtime.InteropServices.COMException (0x80070520): A specified logon session does not exist. It may already have been terminated. (Exception from HRESULT: 0x80070520)" src="http://nlsimmons.com/SQLScraps/wp-content/uploads/2013/08/ssl_error.jpg" align="center" /><br />
<br/><br />
You get this error when your certificate isn&#8217;t quite set up correctly. This is a real pain, considering makecert will happily create a certificate that SSRS cannot subsequently use. So, even though you get a certificate that can be imported via the Certificates snap-in for MMC and that shows up in SSRS Configuration Manager, SSRS cannot actually bind the SSL certificate!</p>
]]></content:encoded>
			<wfw:commentRss>http://nlsimmons.com/?feed=rss2&#038;p=259</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MSDTC, Triggers, and Firewalls. Oh my!</title>
		<link>http://nlsimmons.com/?p=257</link>
		<comments>http://nlsimmons.com/?p=257#comments</comments>
		<pubDate>Thu, 01 Aug 2013 22:52:51 +0000</pubDate>
		<dc:creator><![CDATA[]]></dc:creator>
				<category><![CDATA[SQL Profiler]]></category>
		<category><![CDATA[Troubleshooting]]></category>
		<category><![CDATA[Triggers]]></category>
		<category><![CDATA[Wireshark]]></category>

		<guid isPermaLink="false">http://nlsimmons.com/SQLScraps/?p=257</guid>
		<description><![CDATA[One Experience Troubleshooting Transactions Over Linked Servers The Backstory This client had been plagued by poor table and index design,&#8230;]]></description>
				<content:encoded><![CDATA[            <script type="text/javascript" src="http://nlsimmons.com/wp-content/plugins/wordpress-code-snippet/scripts/shBrushSql.js"></script>
<h3>One Experience Troubleshooting Transactions Over Linked Servers</h3>
<h4>The Backstory</h4>
<p>This client had been plagued by poor table and index design, which by way of regular application use, caused logical fragmentation of the indexes to go from under 5% to over 90% in a relatively short amount of time.  So I built a solution using Service Broker to check index fragmentation and REORGANIZE or REBUILD where necessary.  The indexes are checked for fragmentation weekly.  Not long after this solution was in place, I got word that a few indexes were still getting too fragmented, even with weekly maintenance on the indexes.  Before implementing the index solution, it would be common for me to manually rebuild the indexes on a table when performance was degraded.  Almost always, this first step resolved the performance issue. <span id="more-257"></span></p>
<h4>The Event</h4>
<p>Two principles come to mind when I recount this specific troubleshooting episode: </p>
<p><b> &#8211; Assume nothing </b><br />
<b> &#8211; Remember what tools are in your toolbox, and use them </b>  </p>
<p>Usually when we don&#8217;t adhere to these principles during troubleshooting, the effort takes much longer than is necessary.  Such is the case for me when I was tasked with troubleshooting application timeouts due to long-running UPDATE and SELECT queries.  The table in question is a very small table, with 460 total records.  I checked BLOCKED_PROCESS_REPORT events and found hundreds of statements waiting on the table&#8217;s Primary Key.  I&#8217;ve seen this a lot lately due to fragmentation, so I checked out logical fragmentation on the table indexes.  I like to use the following query to quickly check fragmentation on a specific table in a database.</p>
<p><pre class="brush: sql">SELECT i.name, d.avg_fragmentation_in_percent 
FROM sys.dm_db_index_physical_stats(DB_ID(), OBJECT_ID('&lt;TABLE_NAME&gt;'),NULL, NULL, NULL) d
INNER JOIN sys.indexes i ON d.[object_id] = i.[object_id] AND d.index_id = i.index_id
WHERE d.alloc_unit_type_desc = 'IN_ROW_DATA'</pre></p>
<p>In this specific case, the fragmentation wasn&#8217;t nearly high enough to raise concerns.  At this point, I had prematurely convinced myself that this was a fragmentation issue, even if logical fragmentation wasn&#8217;t over 10%.  Next I used the sysinternals tool contig to check physical fragmentation on the database data file.  contig showed this file to be in 79 fragments, a nod to the days of 1 MB auto-growth.  I got permission from the client to drop the database and restore it from backup.  Once I did this, contig showed the .MDB was in 1 fragment.  The only problem was that this didn&#8217;t solve my performance issue.  In fact, it had no noticeable effect.  Next I thought that maybe the application code had gone awry, submitting the same or similar query against the same record multiple times from multiple SPIDs.  This is also something I&#8217;ve seen before at this client.  So, I decided to fire up SQL Server Profiler to see if that would help figure out the issue.  </p>
<p>Sure enough, once I dialed in the trace properties and started the trace, I could see plain as day what was going on.  The table in question has a trigger on it that updates a table over a linked server.  I started running some test update statements across the same linked server but I didn&#8217;t get back any errors.  This is when you need to remember that triggers fire in the same transaction as the statement that caused the trigger to fire.  The key word here is &#8220;transaction.&#8221;  My test updates were not firing inside of a transaction.  As soon as I ran the updates inside of a transaction, I got back errors.  Furthermore, it was taking over a minute of execution time before encountering the error.  The application timeout setting is at 30 seconds and this is why the developers were getting a .NET timeout error instead of the SQL Server error messages I was getting.   </p>
<p>Okay, so I know what&#8217;s wrong.  It seems the firewall is blocking MSDTC traffic.  I used Wireshark to look at the traffic going between the server and the linked server to be updated, filtering out anything that wasn&#8217;t MSDTC traffic or traffic to the linked server IP.  This gave me a clear picture of what IP the MSDTC traffic was going out on.  After checking the firewall on the linked server, I saw the IP where the traffic was coming from wasn&#8217;t in the allow list for the MSDTC rule.  I added the IP, updated group policies, and BAM! the trigger succeeded.   </p>
<p>I came in the next day to reports of the SAME EXACT ISSUE!  So what&#8217;s going on?  The server initiating the linked server update is actually a clustered SQL Server instance running on a 2 node Windows cluster.  In addition to this clustered SQL Server instance, the same Windows cluster hosts 3 additional SQL Server clusters.  This specific set up has 14 IPs in total.  On this particular morning, the MSDTC traffic had switched to a different IP, and was going out over an IP not even associated with the SQL Server instance initiating the update!  Furthermore, this IP wasn&#8217;t the one associated with the clustered MSDTC service.  So, I ended up having to add all 14 IPs to the firewall rule.  Problem solved.  No new reports of this issue now.  Whew.</p>
<p>Now I&#8217;m on a quest to understand why MSDTC traffic goes out any old IP!  If I find the answer to this, you can bet there will be another blog post on that topic.  Until then, enjoy!</p>
]]></content:encoded>
			<wfw:commentRss>http://nlsimmons.com/?feed=rss2&#038;p=257</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Redundant Indexes Part 1: Identifying Redundant Indexes That Are Not Used To Service Queries</title>
		<link>http://nlsimmons.com/?p=255</link>
		<comments>http://nlsimmons.com/?p=255#comments</comments>
		<pubDate>Thu, 25 Jul 2013 22:31:42 +0000</pubDate>
		<dc:creator><![CDATA[]]></dc:creator>
				<category><![CDATA[Database Administration]]></category>
		<category><![CDATA[Indexing]]></category>
		<category><![CDATA[T-SQL]]></category>

		<guid isPermaLink="false">http://nlsimmons.com/SQLScraps/?p=255</guid>
		<description><![CDATA[Overview Indexes are a powerful concept in any RDBMS. They can dramatically increase the performance of queries, but they can&#8230;]]></description>
				<content:encoded><![CDATA[            <script type="text/javascript" src="http://nlsimmons.com/wp-content/plugins/wordpress-code-snippet/scripts/shBrushSql.js"></script>
<h4>Overview</h4>
<p>Indexes are a powerful concept in any RDBMS. They can dramatically increase the performance of queries, but they can also become overly burdensome. Enter redundant, unused indexes. Every index is maintained by SQL Server after every INSERT, UPDATE, or DELETE statement. So, if there are indexes on a table that are redundant, and aren&#8217;t used to service any SELECT queries, then it is a waste of resources for SQL Server to store and maintain those indexes. This maintenance unnecessarily slows down INSERT, UPDATE, and DELETE statements. So when I found literally dozens of these indexes in each production database in this specific client&#8217;s environment, suffice it to say I was shocked.</p>
<p>Naturally, I took it upon myself to delete these indexes.  Doing this manually would be extremely time consuming, as there were well over a thousand of these across all production databases.  I cobbled together code from my own mind and a few disparate sources across the internet and created a stored procedure to report back to the caller any redundant and unused indexes in the database in which the procedure was run.  I created the procedure in the [master] database and marked it as a system object.  This way, I only had to deploy the procedure to each instance of SQL Server, and not each individual database.  Without further ado, here is the code for the procedure.  Below the full procedure code is a discussion of the parts.   </p>
<h4>Procedure Code</h4>
<p><pre class="brush: sql">USE [master]
GO

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE PROCEDURE [dbo].[sp_RedundantIndexes]
AS 
BEGIN

	SET NOCOUNT ON;

	DECLARE @ID				INT
	DECLARE @DatabaseName	NVARCHAR(128)
	DECLARE @TableName		NVARCHAR(128)
	DECLARE @IndexName		NVARCHAR(128)
	DECLARE @IndexID		INT
	DECLARE @ObjectID		BIGINT
	DECLARE @LastSvcRestart	DATETIME

	CREATE TABLE #indexdetails (
		  ID INT IDENTITY(1,1) PRIMARY KEY CLUSTERED
		, ServerName NVARCHAR(128)
		, DatabaseName NVARCHAR(128)
		, TableName NVARCHAR(128)
		, IndexName NVARCHAR(128)
		, IndexID INT
		, IndexType NVARCHAR(128)
		, [Object_ID] BIGINT
		, MostRecentDate DATETIME
		, IsPK BIT NULL
		, StatsSum INT NULL
		, IsSubset BIT DEFAULT(0)
		, IsRedundant BIT DEFAULT(0)
		, IsMaster BIT DEFAULT(0)
		, Processed BIT DEFAULT(0)
	)
	
	--Get time of last SQL Server service restart.  Whenever SQL Server is restarted, tempdb is recreated
	SELECT @LastSvcRestart = create_date FROM sys.databases WHERE name = 'tempdb';
	
	IF ( DATEDIFF(HH, @LastSvcRestart, GETDATE()) &lt;= 96 )
	BEGIN
		SELECT TOP 0 ServerName, DatabaseName, TableName, IndexName 
		FROM #indexdetails;
		
		RETURN;
	END

	INSERT #indexdetails (ServerName, DatabaseName, TableName, IndexName, IndexType, IndexID, [Object_ID], MostRecentDate, IsPK)
	SELECT DISTINCT 
		  CAST(SERVERPROPERTY('MachineName') AS NVARCHAR(128)) --@@SERVERNAME
		, DB_NAME()
		, o.name AS TableName
		, i.name AS IndexName
		, i.type_desc AS IndexType
		, i.index_id
		, o.[object_id]
		, CASE WHEN o.modify_date &gt; o.create_date THEN o.modify_date ELSE o.create_date END AS MostRecentDate
		, CASE WHEN kc.[type] = 'PK' THEN 1 ELSE 0 END AS IsPK
	FROM		sys.indexes i 
	INNER JOIN	sys.objects o ON i.[object_id] = o.[object_id]
	INNER JOIN	sys.index_columns ic ON i.[object_id] = ic.[object_id] AND i.[index_id] = ic.[index_id]
	LEFT  JOIN	sys.key_constraints kc ON i.[object_id] = kc.[parent_object_id] AND i.name = kc.name
	WHERE o.type_desc &lt;&gt; 'SYSTEM_TABLE'
	ORDER BY o.name, i.name

	WHILE EXISTS(SELECT 1 FROM #indexdetails WHERE processed = 0)	-- Still records to process
	BEGIN
		SELECT TOP 1 
			  @ID = ID
			, @DatabaseName = DatabaseName
			, @TableName = TableName
			, @IndexName = IndexName
			, @IndexID = IndexID
			, @ObjectID = [Object_ID]
		FROM #indexdetails
		WHERE processed = 0

		UPDATE #indexdetails 
		SET		processed = 1 ,
				IsSubset = (SELECT ISNULL(MAX(IndexStatus), 0) FROM (
								SELECT CASE WHEN NOT EXISTS(
											SELECT index_column_id, column_id, is_descending_key, is_included_column FROM sys.index_columns WHERE index_id = @IndexID AND [object_id] = OBJECT_ID(@TableName)
											EXCEPT
											(
												SELECT index_column_id, column_id, is_descending_key, is_included_column FROM sys.index_columns WHERE index_id = @IndexID AND [object_id] = OBJECT_ID(@TableName)
												INTERSECT
												SELECT index_column_id, column_id, is_descending_key, is_included_column FROM sys.index_columns WHERE index_id = t2.index_id AND [object_id] = OBJECT_ID(@TableName)
											)
										)
										THEN 1 ELSE 0 END AS IndexStatus	--1 implies &quot;IsContainedIn&quot; and 0 implies &quot;IndependentOf&quot;
										, t2.index_id
								FROM sys.indexes t2
								WHERE t2.[object_id] = OBJECT_ID(@TableName) AND t2.[index_id] &lt;&gt; @IndexID
								) z),
				IsMaster = (SELECT ISNULL(MAX(IndexStatus), 0) FROM (
								SELECT CASE WHEN NOT EXISTS(
											SELECT index_column_id, column_id, is_descending_key, is_included_column FROM sys.index_columns WHERE index_id = @IndexID AND [object_id] = OBJECT_ID(@TableName)
											EXCEPT
											SELECT index_column_id, column_id, is_descending_key, is_included_column FROM sys.index_columns WHERE index_id = t2.index_id AND [object_id] = OBJECT_ID(@TableName)
											)
										AND NOT EXISTS(
											SELECT index_column_id, column_id, is_descending_key, is_included_column FROM sys.index_columns WHERE index_id = t2.index_id AND [object_id] = OBJECT_ID(@TableName)
											EXCEPT
											SELECT index_column_id, column_id, is_descending_key, is_included_column FROM sys.index_columns WHERE index_id = @IndexID AND [object_id] = OBJECT_ID(@TableName)
										)
										THEN 1 ELSE 0 END AS IndexStatus	--1 implies &quot;IsDuplicate&quot; and 0 implies &quot;NotExactMatch&quot;
										, t2.index_id
								FROM sys.indexes t2
								INNER JOIN #indexdetails n ON t2.index_id = n.indexID AND t2.[object_id] = n.[object_id] AND n.IsMaster = 0
								WHERE t2.[object_id] = OBJECT_ID(@TableName) AND t2.[index_id] &lt;&gt; @IndexID 
							) z)
		WHERE ID = @ID;
	
		;WITH stats AS (
			SELECT s.database_id, s.[object_id], s.index_id, SUM(s.user_seeks + s.user_scans + s.user_lookups) AS StatsSum
			FROM sys.dm_db_index_usage_stats s
			WHERE s.database_id = DB_ID()
			GROUP BY  s.database_id, s.[object_id], s.index_id
		)

		UPDATE i
		SET StatsSum = ISNULL(s.StatsSum,0)
		FROM		#indexdetails i
		LEFT JOIN	stats s ON s.database_id = DB_ID(i.DatabaseName) AND s.[object_id] = OBJECT_ID(i.TableName) AND s.index_id = i.IndexID

		UPDATE #indexdetails SET isRedundant = 1 WHERE isSubset = 1 AND ISNULL(statsSum, 0) = 0 AND ID = @ID

	END
	
	SELECT ServerName, DatabaseName, TableName, IndexName, IndexType
	FROM #indexdetails 
	WHERE	isRedundant = 1
	AND		IsPK &lt;&gt; 1
	AND		IsMaster &lt;&gt; 1

END
GO</pre></p>
<h4>Code Guts</h4>
<p><b>Lines 1 &#8211; 40:</b>  The code in the first 40 lines is very straightforward.  The table #indexdetails will store all of the information needed during execution of the stored procedure.  The variables will be used when iterating over each record in #indexdetails.</p>
<p><b>Lines 41 &#8211; 50:</b>  This code causes the stored procedure to return 0 results if it is run within 4 days of the last restart of SQL Server.  For the client&#8217;s specific needs, this 4 day restriction is sufficient to ensure that we don&#8217;t return indexes that are used less frequently but nevertheless are important.  Most people may find that this time frame is far too short.  If your organization has reporting queries that only hit an index once a month, quarter, or year, this restriction will need to be adjusted accordingly.  When you combine the fact that index use stats are reset at the time of a SQL Server restart and the fact that a query may not use an index but once in a great while, it may not be possible to identify unused indexes.  This procedure is not used on databases where this is the case.</p>
<p><b>Lines 52 &#8211; 68:</b>  This INSERT statement is straight-forward and populates #indexdetails with basic information on every index in the database (excluding those on system tables).</p>
<p><b>Lines 70 &#8211; 132:</b>  These lines are for the WHILE loop that does the majority of work in the procedure and the most interesting pieces are discussed in further detail below.  This section iterates over every index listed in #indexdetails one record at a time.</p>
<p><b>Lines 84 &#8211; 98:</b>  This subquery uses an additional correlated subquery to check if the current index is a subset of another index on the same table. First index_column_id, column_id, is_descending_key, and is_included_column values are obtained from sys.index_columns for the current index.  Next these values are Intersected with the same values for every other index on the same table.  This is what makes the inner subquery correlated.  The first result is then EXCEPTed from the second result set.  If nothing is returned from this EXCEPT statement, then the index in question is a subset of another index on the same table.  The idea here is to find any index on the same table that has the same set of values for index_column_id, column_id, is_descending_key, and is_included_column as the current index being analyzed.  When we encounter one, we know that the current index can be seen as a subset of another index.</p>
<p><b>Lines 99 &#8211; 115:</b>  Like the subquery just discussed, this one has a correlated subquery as well.  This subquery checks to see if the current index should be considered a &#8220;master&#8221; index.  Here, master index must be defined in the event that N indexes are identical.  This will prevent the procedure from returning all of those indexes as redundant, when only N-1 indexes are redundant.  That is, if 3 indexes have identical definitions, then only 2 are redundant, not all 3.  This subquery uses EXCEPT but not INTERSECT.  EXCEPT is used to retrieve index_column_id, column_id, is_descending_key, and is_included_column values that are present for the current index that are not in any of the other indexes on the same table, checking one index at a time.  At the same time, the code looks at index_column_id, column_id, is_descending_key, and is_included_column  values that are present in the correlated set (minus any indexes already marked IsMaster = 1) that are not present in the result set for the current index.  If both EXECPT queries return empty sets, the current index is marked as the master.  </p>
<p><b>Lines 118 &#8211; 128:</b>  This query uses a CTE and the DMV sys.dm_db_index_usage_stats to SUM the values for user_seeks,  user_scans, and user_lookups.  This value is stored as StatsSum in #indexdetails.</p>
<p><b>Line 130:</b>  This UPDATE sets the IsRedundant flag for indexes that have been marked IsSubset = 1 and have no combined user_seeks,  user_scans, or user_lookups. </p>
<p><b>Lines 134 &#8211; 138:</b>  This simple SELECT returns records to the caller of sp_RedundantIndexes.  The records that are returned are ones that are marked as redundant but aren&#8217;t Primary Keys and aren&#8217;t marked as a master index.</p>
<h4>Limitations</h4>
<p>There are some limitations to this code.  The first and foremost limitation concerns the use stats for the index.  If an index is used only rarely, but is critical when it does get used, then this code may incorrectly mark it as unused.  That said, only <b>redundant AND unused</b> indexes are returned.  This reduces the possibility that the procedure returns a needed index.  The other limitation here is that this doesn&#8217;t look at primary keys.  The reason for this is that the client I built this for often times uses a single column as a primary key, and that column is then duplicated in other indexes that index additional columns, with the primary key column being the leading column.  These primary keys may be considered redundant indexes, but because they are primary keys, they should not be dropped, as other tables may have foreign keys pointing back to them.</p>
<h4>What&#8217;s Next</h4>
<p>In Redundant Indexes Part 2, I show a simple Powershell script that I use to take action on the indexes returned by sp_RedundantIndexes.</p>
]]></content:encoded>
			<wfw:commentRss>http://nlsimmons.com/?feed=rss2&#038;p=255</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Altering Stored Procedure Families Programatically</title>
		<link>http://nlsimmons.com/?p=212</link>
		<comments>http://nlsimmons.com/?p=212#comments</comments>
		<pubDate>Thu, 29 Mar 2012 18:41:12 +0000</pubDate>
		<dc:creator><![CDATA[]]></dc:creator>
				<category><![CDATA[Stored Procedures]]></category>
		<category><![CDATA[T-SQL]]></category>
		<category><![CDATA[Automation]]></category>
		<category><![CDATA[Script]]></category>
		<category><![CDATA[Stored Procedure]]></category>

		<guid isPermaLink="false">http://nlsimmons.com/SQLScraps/?p=212</guid>
		<description><![CDATA[Alter every stored procedure in a database that fits a given criteria Recently I had to search for, and update,&#8230;]]></description>
				<content:encoded><![CDATA[            <script type="text/javascript" src="http://nlsimmons.com/wp-content/plugins/wordpress-code-snippet/scripts/shBrushSql.js"></script>
<h2>Alter every stored procedure in a database that fits a given criteria</h2>
<p>Recently I had to search for, and update, hard-coding server references for every stored procedure in a database.  This amounted to altering over 60 stored procedures.  Instead of going through each stored procedure, one-by-one, I used the following code to accomplish the task:</p>
<p><pre class="brush: sql">BEGIN TRANSACTION

CREATE TABLE #ProcsToChange (
	ProcName NVARCHAR(MAX) NULL
)

CREATE TABLE #tblHolding (
	ID INT IDENTITY(1,1) NOT NULL,
	proctext NVARCHAR(MAX) NULL
)

INSERT #ProcsToChange (ProcName)
SELECT DISTINCT OBJECT_NAME(sc.id)
FROM syscomments sc
INNER JOIN sysobjects so 
ON sc.id = so.id
WHERE sc.text LIKE '%[OldServer]%'
ORDER BY OBJECT_NAME(sc.id); 

DECLARE @newProcText NVARCHAR(MAX)
DECLARE @proc NVARCHAR(MAX)

DECLARE c CURSOR FOR
SELECT ProcName FROM #ProcsToChange;

OPEN c;
FETCH NEXT FROM c INTO @proc 

WHILE @@FETCH_STATUS = 0
BEGIN

	INSERT #tblHolding (proctext)
	EXEC sp_helptext @proc
	
	PRINT @proc
	SELECT @newProcText = NULL;
	SELECT @newProcText = COALESCE(@newProcText,'') + proctext FROM #tblHolding ORDER BY ID ASC
	SELECT @newProcText = REPLACE(@newProcText, 'CREATE PROCEDURE', 'ALTER PROCEDURE');
	SELECT @newProcText = REPLACE(@newProcText, '[OldServer]', '[NewServer]');

	--PRINT @newProcText;
	EXEC sp_executesql @newProcText;

	TRUNCATE TABLE #tblHolding
	
	FETCH NEXT FROM c INTO @proc
	
END
CLOSE c;
DEALLOCATE c;

DROP TABLE #ProcsToChange
DROP TABLE #tblHolding

--ROLLBACK
COMMIT
</pre></p>
<p>In order to grab only the stored procedures fitting your criteria, simply manipulate the WHERE clause that is used to filter data being inserted into #ProcsToChange.  </p>
<p>This code should be thoroughly tested in a test environment before using in production.  This code is provided as-is, with no warranty whatsoever.  </p>
<p>
<script type="text/javascript"><!--
google_ad_client = "ca-pub-6201556538459768";
/* SQLScraps - short */
google_ad_slot = "3929371169";
google_ad_width = 468;
google_ad_height = 60;
//-->
</script><br />
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></p>
]]></content:encoded>
			<wfw:commentRss>http://nlsimmons.com/?feed=rss2&#038;p=212</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Signing CLR Assemblies</title>
		<link>http://nlsimmons.com/?p=181</link>
		<comments>http://nlsimmons.com/?p=181#comments</comments>
		<pubDate>Mon, 06 Dec 2010 18:04:40 +0000</pubDate>
		<dc:creator><![CDATA[]]></dc:creator>
				<category><![CDATA[Permissions]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[SQL CLR]]></category>

		<guid isPermaLink="false">http://nlsimmons.com/SQLScraps/?p=181</guid>
		<description><![CDATA[So you want to load a CLR assembly into SQL Server.  What&#8217;s that you say?  You DONT want to set&#8230;]]></description>
				<content:encoded><![CDATA[            <script type="text/javascript" src="http://nlsimmons.com/wp-content/plugins/wordpress-code-snippet/scripts/shBrushSql.js"></script>
<p>So you want to load a CLR assembly into SQL Server.  What&#8217;s that you say?  You DONT want to set your database to TRUSTWORTHY?  Well look no further, there is a much more secure alternative to the old TRUSTWORTHY fallback: sign your assemblies with a secure certificate.</p>
<p>While this sounds like a walk in the park, it&#8217;s not very straight-forward.  In this post I will walk you through the steps necessary to get up and running with a signed assembly.  First you&#8217;ll need some additional tools.  You&#8217;ll need the makecert.exe tool, which is part of the Microsoft Windows SDK.  Also needed are the signtool and pvk2pfx tool, both of which are also part of the SDK.  You can download the Windows SDK here: <a href="http://go.microsoft.com/fwlink/?linkid=84091">http://go.microsoft.com/fwlink/?linkid=84091</a>.</p>
<p>Once you&#8217;ve downloaded and installed the SDK, you&#8217;re ready to begin.  First, create the root authority certificate:<br />
<pre class="brush: sql">makecert -sv SignRoot.pvk -cy authority -r signroot.cer -n &quot;CN=Cert Authority&quot;</pre></p>
<p>Once you&#8217;ve created this certificate, use it to create an additional certificate and private key pair.  This is what will be loaded into SQL server.  It is also what you will use to sign compiled DLL files.<br />
<pre class="brush: sql">makecert -m 360 -n &quot;CN=Signing Cert&quot; -iv signroot.pvk -ic signroot.cer -cy end -pe -sv signcert.pvk signcert.cer
</pre></p>
<p>Now convert this certificate into a PFX file for signing assemblies:<br />
<pre class="brush: sql">pvk2pfx -pvk signcert.pvk -spc signcert.cer -pfx signcert.pfx</pre></p>
<p>Once you have the PFX file, you can use it to sign assemblies:<br />
<pre class="brush: sql">signtool sign /f signcert.pfx &quot;YOUR_DLL_HERE&quot;</pre></p>
<p>Load the certificate file created above into SQL Server. Be sure to use the .cer file:<br />
<pre class="brush: sql">USE [master];
GO

CREATE CERTIFICATE CLRCertificate
FROM FILE = 'signcert.cer'
WITH PRIVATE KEY (
	  FILE = 'signcert.pvk'
	, DECRYPTION BY PASSWORD = 'password'
	, ENCRYPTION BY PASSWORD = 'password'
)
</pre></p>
<p>Now that the certificate has been loaded into SQL Server, you need to create a login from that certificate.  This login will need to have the necessarey assembly permissions (unsafe, external_access).<br />
<pre class="brush: sql">CREATE LOGIN AssemblyLoader FROM CERTIFICATE CLRCertificate
REVOKE connect sql FROM AssemblyLoader 
GRANT external access assembly TO AssemblyLoader 
GRANT unsafe assembly TO AssemblyLoader 
</pre><br />
By creating a certificate-based login that has the unsafe assembly and external_access assembly permissions, you need not grant any other login these permissions.  When you go to load a signed assembly, SQL Server sees that there is a login associated with the signing certificate and that the login has the necessary permissions to load assemblies of any permission level.  </p>
<p>There you have it, the assembly is successfully loaded into SQL Server without compromising security by using the TRUSTWORTHY flag.  Enjoy.</p>
]]></content:encoded>
			<wfw:commentRss>http://nlsimmons.com/?feed=rss2&#038;p=181</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Linked Servers: SQL Server 2005 to SQL Server 7</title>
		<link>http://nlsimmons.com/?p=184</link>
		<comments>http://nlsimmons.com/?p=184#comments</comments>
		<pubDate>Wed, 24 Nov 2010 21:58:32 +0000</pubDate>
		<dc:creator><![CDATA[]]></dc:creator>
				<category><![CDATA[Database Administration]]></category>
		<category><![CDATA[Linked Servers]]></category>

		<guid isPermaLink="false">http://nlsimmons.com/SQLScraps/?p=184</guid>
		<description><![CDATA[Today I had the pleasure of creating a linked server on our main production SQL Server 2005 instance. The catch&#8230;]]></description>
				<content:encoded><![CDATA[            <script type="text/javascript" src="http://nlsimmons.com/wp-content/plugins/wordpress-code-snippet/scripts/shBrushSql.js"></script>
<p>Today I had the pleasure of creating a linked server on our main production SQL Server 2005 instance.  The catch is that the server I needed to link was a SQL Server 7 instance.  Here are the steps I took to get the linked server up and running in under 5 minutes flat:</p>
<ol>
<li> Create a new system DSN using the SQL Server (not native client) driver for the server you&#8217;re going to link to.  I used version 6.01.7600.16385.  Walk through the configuration options and modify those that are applicable to your situation.</li>
<li>Right click &#8220;Linked Server&#8221; under Server Objects in Management Studio.</li>
<li>Type a name for the linked server and select &#8220;Other data source.&#8221;</li>
<li>For the Provider, select &#8220;Microsoft OLE DB Provider for ODBC Drivers&#8221;</li>
<li>For Product name, type the name of the DSN you created in step 1.</li>
<li>Data source is the name of the DSN again.</li>
<li>The provider string I used is Data Source=&lt;DSN&gt;;Initial Catalog=tempdb;Integrated Security=SSPI;</li>
<li>Location and Catalog are both blank.</li>
<li>Under the security page, be sure the radio button is selected for &#8220;Be made using the login&#8217;s current security context&#8221;</li>
<li>Set the appropriate options on the Server Options page.</li>
<li>Click OK</li>
</ol>
<p>That&#8217;s it!  You should now have a working linked server setup between SQL Server 2005 and SQL Server 7.</p>
]]></content:encoded>
			<wfw:commentRss>http://nlsimmons.com/?feed=rss2&#038;p=184</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>A Non-Trivial SQL CLR Table Value Function Example</title>
		<link>http://nlsimmons.com/?p=106</link>
		<comments>http://nlsimmons.com/?p=106#comments</comments>
		<pubDate>Mon, 18 Oct 2010 19:43:24 +0000</pubDate>
		<dc:creator><![CDATA[]]></dc:creator>
				<category><![CDATA[SQL CLR]]></category>

		<guid isPermaLink="false">http://nlsimmons.com/SQLScraps/?p=71</guid>
		<description><![CDATA[Overview In this post I would like to demonstrate how to create a simple yet non-trivial CLR table-valued function (TVF).&#8230;]]></description>
				<content:encoded><![CDATA[            <script type="text/javascript" src="http://nlsimmons.com/wp-content/plugins/wordpress-code-snippet/scripts/shBrushSql.js"></script>
            <script type="text/javascript" src="http://nlsimmons.com/wp-content/plugins/wordpress-code-snippet/scripts/shBrushCSharp.js"></script>
<h4>Overview</h4>
<p>In this post I would like to demonstrate how to create a simple yet non-trivial CLR table-valued function (TVF).  There are several examples of CLR TVFs on the net but the majority of them show only a trivial example.  It is very difficult, in some cases impossible, to apply the principles and code in those examples to your own CLR TVF.  The TVF outlined in this post is used to return the IP address for a given host name.  At my shop we needed a way to log the IP a user connects from when they perform certain actions in the database.  .NET can readily perform this work so why not create a TVF to return the IP addresses for a user?  Sometimes this function will get called on a machine with two or more IPs (wireless and physical LAN) so a scalar function will not work here. What makes this TVF non-trivial in my opinion is that it does not just output an already formatted table; you have to build the output. </p>
<p>Without further ado, let&#8217;s dive in.  The following code is full C# source code for the function.  I will first divulge the entire code and then explain the pieces.</p>
<p><pre class="brush: csharp">using System;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Net;                       // Bring in classes necessary for working with IPs
using System.Collections;               // Bring in the IEnumberable defintion
using System.Collections.Generic;       // Bring in the List&lt;T&gt; class

public class Class_mfn_dnslookup
{
    [SqlFunction(Name = &quot;mfn_dnslookup&quot;, FillRowMethodName = &quot;FillRow&quot;, TableDefinition = &quot;IP NVARCHAR(20)&quot;)]
    public static IEnumerable mfn_dnslookup(SqlString hostname)
    {
        string c_hostname = hostname.ToString();
        IPHostEntry host;
        host = Dns.GetHostEntry(c_hostname);

        List&lt;object[]&gt; resultItems = new List&lt;object[]&gt;();

        foreach (IPAddress ip in host.AddressList)
        {
            if (!IPAddress.IsLoopback(ip) &amp;&amp; ip.ToString().IndexOf(&quot;.&quot;) != -1)
            {
                object[] item = new object[1];
                item.SetValue(ip.ToString(), 0);

                resultItems.Add(item);
            }
        }
        return resultItems;
    }

    public static void FillRow(Object obj, out SqlString IP)
    {
        object[] item = (object[])obj;
        IP = (SqlString)item[0].ToString();
    }
};
</pre></p>
<h4>Code Guts</h4>
<p>In line 10 we define the FillRowMethodName which will dictate how the return table is built.  We also define the TableDefinition for the output table.  Note that Line 11 uses the output type IEnumerable.  This type interacts closely with the FillRow method specified earlier.  This also allows for the table to be returned as rows are filled, instead of waiting for all rows to be populated and then returned.  This is particularly handy when returning large data sets that you want to begin viewing right away.  To build a result set, I assign the IP address to an index within an object array of static length (lines 23 and 24).  This array only has the one slot, which is fine since each adapter has only one IP address.  I then add this array to the resultItems list (line 26).  Line 29 returns the resultItems list.  But to where? Or what?  This is where the IEnumerable class comes back into play.  For CLR TVFs, the data being returned (resultItems list in this case) is operated on by the FillRow method, row by row.</p>
<p>So, the resultItems list, when returned, is passed through the FillRow method.  Lines 34 and 35 show how values are set for one output row.  The IP output in these lines corresponds to the [IP] column in the table definition. This happens for each output row in the resultItems list that is passed into the FillRow method.  The results are sent back to the calling client as they are returned.</p>
<h4>Conclusion</h4>
<p>While there are several postings about CLR TVFs, I felt none of them explained their code well enough, namely the FillRow method.  Jonathan Kehayias has a decent post at <a href="http://www.sqlclr.net/Examples/tabid/55/articleType/ArticleView/articleId/20/Default.aspx">SQLCLR.net</a>.  I took the concepts from his post and applied them to my situation.  Hopefully my explanation will help you better understand how to create a meaningful CLR TVF.</p>
]]></content:encoded>
			<wfw:commentRss>http://nlsimmons.com/?feed=rss2&#038;p=106</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Msg 14274, Level 16, State 1, Procedure sp_add_job</title>
		<link>http://nlsimmons.com/?p=152</link>
		<comments>http://nlsimmons.com/?p=152#comments</comments>
		<pubDate>Wed, 13 Oct 2010 22:47:21 +0000</pubDate>
		<dc:creator><![CDATA[]]></dc:creator>
				<category><![CDATA[SQL Server Agent]]></category>

		<guid isPermaLink="false">http://nlsimmons.com/SQLScraps/?p=152</guid>
		<description><![CDATA[Cannot add, update, or delete a job (or its steps or schedules) that originated from an MSX server. Recently I&#8230;]]></description>
				<content:encoded><![CDATA[            <script type="text/javascript" src="http://nlsimmons.com/wp-content/plugins/wordpress-code-snippet/scripts/shBrushSql.js"></script>
            <script type="text/javascript" src="http://nlsimmons.com/wp-content/plugins/wordpress-code-snippet/scripts/shBrushCSharp.js"></script>
<h3>Cannot add, update, or delete a job (or its steps or schedules) that originated from an MSX server.</h3>
<p>Recently I ran into this error on an instance of SQL Server 2005.  There are several articles and blog posts concerning this error on SQL Server 2000 systems, but next to none that discuss the error in SQL Server 2005.  For a list of all of the procedures in MSDB that can throw this error, execute the following query:</p>
<p><pre class="brush: sql">SELECT name 
FROM sys.procedures
WHERE
OBJECT_DEFINITION(OBJECT_ID(name)) LIKE '%RAISERROR(14274%'</pre></p>
<p>In my case, the error came from msdb.dbo.sp_add_job.  To figure out what was causing the error, take a look at the procedure code:</p>
<p><pre class="brush: sql">USE [MSDB]
GO

EXEC sp_helptext sp_add_job;</pre></p>
<p>Do a search for &#8216;RAISERROR(14274&#8242; and you&#8217;ll see the logic that is causing the error.  In the case of sp_add_job, the logic is:</p>
<p><pre class="brush: sql">IF (@job_id IS NULL)
  BEGIN
    -- Assign the GUID
    SELECT @job_id = NEWID()
  END
  ELSE
  BEGIN
    -- A job ID has been provided, so check that the caller is SQLServerAgent (inserting an MSX job)
    IF (PROGRAM_NAME() NOT LIKE N'SQLAgent%')
    BEGIN
      RAISERROR(14274, -1, -1)
      RETURN(1) -- Failure
    END
  END</pre></p>
<p>Here we see that if a non-NULL value for @job_id is passed into sp_add_job from somewhere other than a SQL Agent Job step (e.g. SSMS), you&#8217;ll get the 14274 error.</p>
<p>I hope this helps someone out there!</p>
]]></content:encoded>
			<wfw:commentRss>http://nlsimmons.com/?feed=rss2&#038;p=152</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Scripting Stored Procedure Permissions</title>
		<link>http://nlsimmons.com/?p=104</link>
		<comments>http://nlsimmons.com/?p=104#comments</comments>
		<pubDate>Wed, 06 Oct 2010 09:36:52 +0000</pubDate>
		<dc:creator><![CDATA[]]></dc:creator>
				<category><![CDATA[Permissions]]></category>
		<category><![CDATA[Stored Procedures]]></category>

		<guid isPermaLink="false">http://nlsimmons.com/SQLScraps/?p=7</guid>
		<description><![CDATA[Overview When scripting stored procedure code for deployment to several servers, there are a few things to keep in mind. &#8230;]]></description>
				<content:encoded><![CDATA[            <script type="text/javascript" src="http://nlsimmons.com/wp-content/plugins/wordpress-code-snippet/scripts/shBrushSql.js"></script>
            <script type="text/javascript" src="http://nlsimmons.com/wp-content/plugins/wordpress-code-snippet/scripts/shBrushCSharp.js"></script>
<h4>Overview</h4>
<p>When scripting stored procedure code for deployment to several servers, there are a few things to keep in mind.  One of those things is the permissions associated with the procedure you&#8217;re deploying.  When deploying a brand new stored procedure, the process of assigning permissions is fairly straightforward.  If you need to deploy an update to an existing stored procedure, things get a bit more complicated.  If you simply issue a DROP PROCEDURE call followed by the CREATE PROCEDURE code, you will lose the permissions that are associated with the stored procedure.  Issuing an ALTER PROCEDURE command will preserve the permissions but will cause you trouble if there are places where the procedure doesn&#8217;t yet exist.  To solve this issue, you should script out the permissions on a procedure to a temp table before dropping the procedure.  If there is a chance that the batch will fail, use a persisted table or a global temp table instead.  Once the proc is recreated, run through the permissions and apply them to the proc again.  This way, you&#8217;ve preserved the permissions on the proc.</p>
<p>Now, there are some pitfalls to this approach.  If the proc didn&#8217;t already exist when you run the deploy script, there will obviously be no permissions to script out to a temp table.  So, you might run into the problem where the same proc on two different databases has two sets of permissions.  This might be an issue, but considering the proc didn&#8217;t exist in the first place, it&#8217;s doubtful that it would be an issue.  Nevertheless it is something to keep in mind.</p>
<p>The way to get around this pitfall is to include desired the permissions in the SQL script used to create the procedure.  This way, the newly created proc will have the permissions it had before, in addition to any included in the deployment script.</p>
<h4>The Code</h4>
<p>Here is some sample SQL for scripting the permissions into a temp table:</p>
<p><pre class="brush: sql">IF EXISTS (SELECT * FROM sys.objects WHERE name = 'SampleProc')
BEGIN
	IF OBJECT_ID('tempdb..#tmp_PermissionsWork') IS NOT NULL DROP TABLE #tmp_PermissionsWork
	CREATE TABLE #tmp_PermissionsWork (
		  ID INT IDENTITY(1,1) PRIMARY KEY CLUSTERED NOT NULL 
		, Permission NVARCHAR(MAX) NULL
	)
	
	INSERT #tmp_PermissionsWork (Permission)
	SELECT state_desc + ' ' + permission_name + ' ON ' + OBJECT_NAME(major_id) + ' TO ' + QUOTENAME(USER_NAME(grantee_principal_id)) + ';' FROM sys.database_permissions 
	WHERE major_id = OBJECT_ID('SampleProc')

	DROP PROCEDURE SampleProc
END
GO

CREATE PROCEDURE SampleProc
AS
...
GO

DECLARE @cmd NVARCHAR(MAX)

WHILE EXISTS(SELECT 1 FROM #tmp_PermissionsWork)
BEGIN
	SELECT TOP 1 @cmd = Permission
	FROM #tmp_PermissionsWork
	
	EXEC sp_executesql @cmd;
	
	DELETE FROM #tmp_PermissionsWork 
	WHERE Permission = @cmd
END
GO</pre></p>
<pre>
IF EXISTS (SELECT * FROM sys.objects WHERE name = 'SampleProc')
BEGIN
	IF OBJECT_ID('tempdb..#tmp_PermissionsWork') IS NOT NULL DROP TABLE #tmp_PermissionsWork
	CREATE TABLE #tmp_PermissionsWork (
		  ID INT IDENTITY(1,1) PRIMARY KEY CLUSTERED NOT NULL 
		, Permission NVARCHAR(MAX) NULL
	)
	
	INSERT #tmp_PermissionsWork (Permission)
	SELECT state_desc + ' ' + permission_name + ' ON ' + OBJECT_NAME(major_id) + ' TO ' + QUOTENAME(USER_NAME(grantee_principal_id)) + ';' FROM sys.database_permissions 
	WHERE major_id = OBJECT_ID('SampleProc')

	DROP PROCEDURE SampleProc
END
GO

CREATE PROCEDURE SampleProc
AS
...
GO

DECLARE @cmd NVARCHAR(MAX)

WHILE EXISTS(SELECT 1 FROM #tmp_PermissionsWork)
BEGIN
	SELECT TOP 1 @cmd = Permission
	FROM #tmp_PermissionsWork
	
	EXEC sp_executesql @cmd;
	
	DELETE FROM #tmp_PermissionsWork 
	WHERE Permission = @cmd
END
GO
</pre>
<p>To use this code, swap out &#8216;SampleProc&#8217; with your stored procedure and put your procedure code in place of lines 17 &#8211; 20.</p>
<p><font face=arial size=1>*This code is provided &#8220;AS-IS&#8221; with no warranty of any kind.</font></p>
]]></content:encoded>
			<wfw:commentRss>http://nlsimmons.com/?feed=rss2&#038;p=104</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>How to Kill a Profiler Trace</title>
		<link>http://nlsimmons.com/?p=105</link>
		<comments>http://nlsimmons.com/?p=105#comments</comments>
		<pubDate>Fri, 01 Oct 2010 20:32:20 +0000</pubDate>
		<dc:creator><![CDATA[]]></dc:creator>
				<category><![CDATA[SQL Profiler]]></category>

		<guid isPermaLink="false">http://nlsimmons.com/SQLScraps/?p=28</guid>
		<description><![CDATA[Using Profiler to run a trace can be an invaluable tool for troubleshooting performance problems with T-SQL code. Occasionally something&#8230;]]></description>
				<content:encoded><![CDATA[            <script type="text/javascript" src="http://nlsimmons.com/wp-content/plugins/wordpress-code-snippet/scripts/shBrushSql.js"></script>
            <script type="text/javascript" src="http://nlsimmons.com/wp-content/plugins/wordpress-code-snippet/scripts/shBrushCSharp.js"></script>
<p>Using Profiler to run a trace can be an invaluable tool for troubleshooting performance problems with T-SQL code.  Occasionally something happens to the connection between Profiler and the SQL Server where the trace is running and you can no longer manage the trace from the profiler window.  Because an errant trace can impact performance, this situation needs to be rectified ASAP.  To get information on the traces being run on a server, you can issue the following query.</p>
<p><pre class="brush: sql">SELECT * FROM ::fn_trace_getinfo(NULL)</pre></p>
<p>The output is a table and looks like the following: </p>
<p></p>
<div class="indented">
<table>
<tr>
<td><b>traceid</b></td>
<td><b>property</b></td>
<td><b>value</b></td>
</tr>
<tr>
<td>1</td>
<td>1</td>
<td>2</td>
</tr>
<tr>
<td>1</td>
<td>2</td>
<td>PathToFile</td>
</tr>
<tr>
<td>1</td>
<td>3</td>
<td>20</td>
</tr>
<tr>
<td>1</td>
<td>4</td>
<td>NULL</td>
</tr>
<tr>
<td>1</td>
<td>5</td>
<td>1</td>
</tr>
</table>
</div>
<p>
</p>
<p>The [traceid] column of the result set is the ID of the trace.  You&#8217;ll need to find the errant trace ID from this column.  Check around with other folks that may be running traces in the event that the above query returns more than 2 traceid values.  The default trace will always have traceid = 1.  For more information on fn_trace_getinfo, see the TechNet article <a href="http://technet.microsoft.com/en-us/library/ms173875.aspx">here</a>.</p>
<p>Once you know the traceid of the errant trace, you can stop it and remove it from the server.  To do that, you&#8217;ll need to use the sp_trace_setstatus stored procedure.  The syntax is as follows:</p>
<p><pre class="brush: sql">EXEC sp_trace_setstatus @traceid = 2 , @status = 0
EXEC sp_trace_setstatus @traceid = 2 , @status = 2</pre></p>
<p>The @status parameter is what is used by the stored procedure to start, stop, or remove the trace.  A status of 0 indicates the &#8216;stop&#8217; action is to be taken while a status of 2 indicates that the trace is to be removed.  Using @status=1  starts the specified trace.  Additional details regarding the sp_trace_setstatus procedure can be found <a href="http://msdn.microsoft.com/en-us/library/ms176034.aspx">here</a>.</p>
<p>I hope this helps you in the event you need to quickly kill and remove an errant trace.</p>
]]></content:encoded>
			<wfw:commentRss>http://nlsimmons.com/?feed=rss2&#038;p=105</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
