Archive for August, 2009

Pivoting Data Using SQL 2005 (Part 3)

Monday, August 31st, 2009

If you have been following this series, I have been focusing on many different aspects of dynamic pivoting using SQL 2005. In the first part of this series,http://jahaines.blogspot.com/2009/08/pivoting-data-using-sql-2005-part-1.html, I focused on creating a static pivot query. In the second part of this series, http://jahaines.blogspot.com/2009/08/pivoting-data-using-sql-2005-part-2.html,I focused on creating a dynamic pivot, where the pivot values were unknown. We are now going to ramp up this series with a completely dynamic pivot. In this series, will will pivot data where the columns are stored as rows in a table and the values are not known.

In this example, we will be looking at employee sales. Let’s start by creating and populating our sample tables.

SET NOCOUNT ON
GO
 
IF OBJECT_ID('tempdb..#Pivot_Columns') IS NOT NULL
BEGIN
    DROP TABLE #Pivot_Columns;
END
GO
 
CREATE TABLE #Pivot_Columns(
ColId INT PRIMARY KEY,
TableName VARCHAR(25),
ColName VARCHAR(25)
);
GO
 
INSERT INTO #Pivot_Columns VALUES (1,'#Sales','YearMonth');
INSERT INTO #Pivot_Columns VALUES (2,'#Sales','SalesAmt');
 
IF OBJECT_ID('tempdb..#EmpSales') IS NOT NULL
BEGIN
    DROP TABLE #EmpSales;
END
GO
 
CREATE TABLE #EmpSales(
EmpSalesId INT IDENTITY(1,1) PRIMARY KEY,
EmpId INT,
SalesId INT,
ColId INT,
ColTxt VARCHAR(50)
);
GO
 
INSERT INTO #EmpSales VALUES (1,1,1,'2008-08');
INSERT INTO #EmpSales VALUES (1,1,2,'10000');
INSERT INTO #EmpSales VALUES (2,3,1,'2008-07');
INSERT INTO #EmpSales VALUES (2,3,2,'8500.25');
INSERT INTO #EmpSales VALUES (3,4,1,'2008-08');
INSERT INTO #EmpSales VALUES (3,4,2,'5999.99');

Now we have our table, let’s have a look at an example.

DECLARE @sql NVARCHAR(MAX),
        @Pivot_Cols VARCHAR(MAX)
    
--The code below reads through all the data in the table and
--builds a distinct column list.
SELECT 
    @Pivot_Cols = COALESCE(@Pivot_Cols + ',','') + QUOTENAME(ColName)
FROM #Pivot_Columns
GROUP BY QUOTENAME([ColName])
ORDER BY QUOTENAME(ColName) ASC
 
SELECT @sql = N'
SELECT 
    EmpId,' + @Pivot_Cols + '
FROM(
    SELECT e.EmpId,    e.ColTxt,pc.ColName
    FROM #EmpSales e
    INNER JOIN #Pivot_Columns pc ON e.ColId = pc.ColId
) AS dataToPivotTable
PIVOT(
    MAX(ColTxt) --Aggregate Function
    FOR [ColName]-- Column To Pivot
    IN (' + @Pivot_Cols + ') --Values to Pivot
) AS whatToPivotOnTable;'
 
--PRINT @sql
EXEC sp_executesql @sql
GO

Results:

image

As you can see from the screenshot above, we have successfully pivoting the rows of data into columns. Is this all that there is to dynamic pivoting?…..not quite. If you look closely at the example above you will notice that we are using the MAX aggregate to get the maximum of each column. This is fine if you expect a single set of rows, but what do you think will happen when an employee has more than one set of rows? The short answer is the data would be rendered invalid. The data may become invalid because the maximum YearMonth value, may be from a different month than the maximum SalesAmt value. To demonstrate this behavior, we will add a new set of rows into our table.

INSERT INTO #EmpSales VALUES (1,2,1,'2008-09');
INSERT INTO #EmpSales VALUES (1,2,2,'1');

Now execute the same query and your results should look like the screenshot below. The sales amount column has a maximum value of 10,000 and the YearMonth is 2008-09. If you look carefully at the data we inserted, the SalesAmt value should be 1, for 2008-09, not 10,000.

image

So how do we resolve this issue? Let’s press on. We are going to modify our query a bit.


DECLARE @sql NVARCHAR(MAX),
        @Pivot_Cols VARCHAR(MAX)
    
--The code below reads through all the data in the table and
--builds a distinct column list.
SELECT 
    @Pivot_Cols = COALESCE(@Pivot_Cols + ',','') + QUOTENAME(e.ColTxt)
FROM #EmpSales e
INNER JOIN [#Pivot_Columns] pc
    ON e.[ColId] = pc.[ColId]
WHERE pc.[ColName] = 'YearMonth'
GROUP BY QUOTENAME(e.ColTxt)
ORDER BY QUOTENAME(e.ColTxt)
 
SELECT @sql = N'
SELECT 
    EmpId,' + @Pivot_Cols + '
FROM(
    SELECT 
        e.EmpId,
        e.ColTxt as MonthYear,
        CONVERT(NUMERIC(9,2),e2.[ColTxt]) AS [ColTxt]
     FROM #EmpSales e INNER JOIN #EmpSales e2 ON e.EmpId = e2.EmpId AND e.SalesId = e2.SalesId 
     INNER JOIN #Pivot_Columns pc ON pc.ColID = e.[ColId]
     INNER JOIN #Pivot_Columns pc2 ON pc2.ColId = e2.[ColId]
     WHERE pc.ColName = ''YearMonth'' AND pc2.ColName = ''SalesAmt''
) AS dataToPivotTable
PIVOT(
    SUM([ColTxt]) --Aggregate Function
    FOR [MonthYear]-- Column To Pivot
    IN (' + @Pivot_Cols + ') --Values to Pivot
) AS whatToPivotOnTable;'
 
PRINT @sql
EXEC sp_executesql @sql
GO

.csharpcode, .csharpcode pre
{
 font-size: small;
 color: black;
 font-family: consolas, "Courier New", courier, monospace;
 background-color: #ffffff;
 /*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
 background-color: #f4f4f4;
 width: 100%;
 margin: 0em;
}
.csharpcode .lnum { color: #606060; }
.csharpcode, .csharpcode pre
{
 font-size: small;
 color: black;
 font-family: consolas, "Courier New", courier, monospace;
 background-color: #ffffff;
 /*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
 background-color: #f4f4f4;
 width: 100%;
 margin: 0em;
}
.csharpcode .lnum { color: #606060; }
.csharpcode, .csharpcode pre
{
 font-size: small;
 color: black;
 font-family: consolas, "Courier New", courier, monospace;
 background-color: #ffffff;
 /*white-space: pre;*/
}
.csharpcode pre { margin: 0em; }
.csharpcode .rem { color: #008000; }
.csharpcode .kwrd { color: #0000ff; }
.csharpcode .str { color: #006080; }
.csharpcode .op { color: #0000c0; }
.csharpcode .preproc { color: #cc6633; }
.csharpcode .asp { background-color: #ffff00; }
.csharpcode .html { color: #800000; }
.csharpcode .attr { color: #ff0000; }
.csharpcode .alt
{
 background-color: #f4f4f4;
 width: 100%;
 margin: 0em;
}
.csharpcode .lnum { color: #606060; }

The code is very similar in most respects, but the biggest difference is I am not pivoting on ColName. Instead, I am going back to the methods we learned in part 2 of this series. The key to dynamically pivoting data is to know your data. Depending on what you are trying to pivot, you may need to aggregate data or simply use the MAX pivot. In this case, we need to use a more powerful pivot statement. In the variable assignment portion of the modifed code, I am strictly querying #EmpSales table, making sure to filter for column “YearMonth.” Within the Pivot code, I select the needed data by using a self referencing join. We need a self join because our next step is to create a row of data that contains the SalesAmt for each EmpId’s salesId. Once we have this information we can use the pivot operator to perform the aggregation.

Results:

image

How about them apples? As you can see, the new pivot reflects the correct totals for each month. We can also create a version of this code that works with prior versions of SQL Server.

DECLARE @Pivot_Cols VARCHAR(MAX)
DECLARE @sql NVARCHAR(MAX)
 
SELECT 
    @Pivot_Cols = COALESCE(@Pivot_Cols + ',','') 
    + N'SUM(CONVERT(NUMERIC(9,2),CASE WHEN pc.[ColName] = ''YearMonth'' AND e.ColTxt =' 
    + QUOTENAME(e.ColTxt,'''') + ' THEN ' + ' e2.[ColTxt] ELSE NULL END)) AS ' 
    + QUOTENAME(e.ColTxt,'''') + CHAR(13)    
FROM #EmpSales e
WHERE e.ColId = 1
GROUP BY e.ColTxt
ORDER BY e.ColTxt
 
SET @sql = N'SELECT e.EmpId,' + CHAR(13)
      + @Pivot_Cols + CHAR(13) 
      + 'FROM #EmpSales e INNER JOIN #EmpSales e2 ON e.EmpId = e2.EmpId AND e.SalesId = e2.SalesId' + CHAR(13) 
      + 'INNER JOIN #Pivot_Columns pc ON pc.ColID = e.[ColId]' + CHAR(13)
      + 'INNER JOIN #Pivot_Columns pc2 ON pc2.ColId = e2.[ColId]' + CHAR(13)
      + 'WHERE pc.ColName = ''YearMonth'' AND pc2.ColName = ''SalesAmt''' + CHAR(13)
      + 'GROUP BY e.[EmpId]'
      
PRINT @sql
EXEC sp_executesql @sql

So there you have it. A completely dynamic method to pivot data. So what have we learned over these last few posts………….. well I hope that you have learned something :-). I hope that you learned pivoting data in SQL Server can be very simple. Pivoting data can sometimes become a little hairy, but is still a powerful and somewhat simplistic solution to implement. <step on soapbox> Hopefully, Microsoft will give us a pivot operator that is somewhat flexible and dynamic. A native dynamic pivot operator allows for more scalable code and reduced SQL injection risk.; however, I really doubt that we will get any dynamic pivot operator, in the near future</step off soapbox>. Anyway, I hope that you have enjoyed reading this series and that you can use this in your environment.

Until next time… happy coding.

Backing up to NUL vs Backup with Truncate only

Monday, August 31st, 2009

Or “It’s 10pm, do you know where your log records are?

Something that I’ve started to see recently is the idea that Backup Log … With Truncate Only, which was deprecated in SQL 2005 and gone in SQL 2008 can be simulated by backing the log up to the NUL device (backup log … to disk = ‘Nul’). Is that true? If so, is that a good idea?

To answer those questions, first we need to explore what the two statements do.

Backup Log With Truncate_Only

When this statement is run against a database in full or bulk-logged recovery, SQL discards the inactive log records for that database. From that point until a full database backup is done, the database is in pseudo-simple recovery, the inactive log records get discarded when a checkpoint runs and any attempt to back the log up results in an error message.

Msg 4214, Level 16, State 1, Line 1
BACKUP LOG cannot be performed because there is no current database backup.

So once the log has been truncated it’s exceedingly clear that the log chain is broken.

Backup Log to disk = ‘Nul’

To understand what this is doing, first we need to understand what Nul is, in the context of a file. It is not just a misspelling of NULL.

DOS, since the earliest days, has had a number of devices that can be treated like files, eg LPT1, COM1 and CON. NUL is another of those devices (virtual files, if you like) and is equivalent to the unix \dev\nul, a ‘file’ that deletes any data that’s written to it. The bitbucket, if you will.

So anything that’s written to NUL is discarded. So when a transaction log backup is made with ‘nul’ as the file destination, SQL dutifully reads over the inactive log records, formats them as for a transaction log backup and hands them off to the operating system which promptly discards the data and sends back an acknowledgement that the data has been written.

So SQL thinks that the log chain is intact. It discards the log records that were sent to Nul as it would after a normal log backup, because it thinks that they were backed up to disk. It does not switch the DB into a pseudo-simple recovery model. Inactive log records are not discarded at a checkpoint and future log backups will succeed.

That’s the main difference. Log records will still be retained after a backup to NUL. Log backups will succeed after a backup to NUL. They’re useless for restoring the DB because there’s a log backup missing, but they’ll succeed.

Replacement?

So, is backup to nul a replacement for backup with truncate_only? No, it’s not. The replacement for Backup Log with Truncate_Only is well documented. It’s switching the database to simple recovery. If the inactive log records in the log file do not need to be retained for recoverability, this is the way to tell SQL to discard them. If log backups need to be made after this, the DB can be switched back to full and a full DB backup taken.

Backup Log to Disk = ‘Nul’ is the same as backing up the log to a file and then deleting the backup file. Nothing more.

Get Rid of RID Lookups

Monday, August 31st, 2009
Takeaway:
If you see an unexpected RID lookup in an execution plan, consider adding a clustered index as a db schema improvement.
RID Lookup

What is a RID lookup?

You may come across the RID lookup operator as part of an execution plan. With Management Studio, RID lookups are displayed with the following icon (sort of … I fancied it up a little).

A RID Lookup is a lookup into a heap using a Row ID. The Row is included with entries in a non-clustered index in order to find the rest of a table's data in a heap. (Remember, with a heap, the table data is stored unordered so a Row ID is needed for the correlation).

The RID Lookup's official docs can be read here at Books Online. But it's light on the details and it references a more thorough explanation of lookups in general which can be found at Craig Freedman's blog here.

They're bad… or at least not good.
When you find an RID Lookup in a query plan, it's a symptom. It indicates a database schema that breaks several rules of thumb. Those rules-of-thumb are:
  1. Each table should have a clustered index (of course there are exceptions but we're dealing with rules-of-thumb here).
  2. A non-clustered index has been created indicating that someone somewhere identified an ordering on one or more columns that made sense for that data.
  3. There is at least one query (i.e. the one that generated the RID Lookup) that needs columns that are not covered by the non-clustered index.
These three points mean a wasted opportunity. A clustered index should have been created. A heap plus a non-clustered index take about the same space as a clustered index. At least logically: the non-clustered pages map to the clustered index's index nodes and the heap's pages map to the clustered index's leaf nodes.

If possible, definitely consider modifying the schema to include a clustered index (After assessing risks of course).

Really? Always?
Well of course no respectable blogger writes in absolutes* and Grant Fritchley has a video here that discusses RID lookups further. He says that when the RID lookup only deals with one row, that the impact is negligible. True, but had the table been designed with a clustered index, the performance would have been no worse.

* Google gives 81,000 results for "SQL Server" and "it depends". Bing gives 1,750,000!

SQL Server Data Compression: It’s a Party!

Monday, August 31st, 2009

When I was in high school, Dad and I lived with his mom, my Grandma Ozar, for a couple of years.  We took care of things around the house and made sure her coffee pot was always full.  She could really down that coffee – at least two pots a day.  (Looking back, if we could have reduced her caffeine consumption, she probably wouldn’t have needed so much Valium.)

I Can Quit Anytime I Want*

I Can Quit Anytime I Want*

Grandma and some friends took a road trip to Las Vegas, and while they were gone, I threw an epic party.  We’re talking 30 gallon garbage cans filled with homemade Jungle Juice.  (I’m one of those reasons she couldn’t completely eliminate the Valium.) A couple hundred of my closest friends had a good old time.

A couple of my less-than-closest friends had a little too good of a time at my expense and started trashing the house.  They walked up the staircase smashing the picture frames of every family photo, then started to throw a couch off the second floor balcony.  My security guys (I’m telling you, it was that good of a party) carried them out before they got too carried away.

Cleaning Up After The Party

The next morning, the very-closest-friends did a fantastic job of getting things back to normal.  We replaced all the picture frame glass, got the dirt out of the sofa, vacuumed the place top to bottom, and finished the Jungle Juice.  I thought we’d hidden all our tracks, but we got busted by the tiniest of clues.

Someone had left beer bottle caps on top of door jambs all around the house.

Amazing!  Who thinks of this stuff?  You know they did it on purpose, too – they were just itching to get me into trouble.  One beer cap on one door jamb, I could understand, but all over the house?  Damn.

I was disinherited for that particular shindig.

I learned a valuable lesson: if you’re not absolutely sure you can clean up every trace of everybody else’s messes, you shouldn’t throw parties.  Sooner or later, somebody you can’t trust is going to show up at your party, and they’re going to do something that’ll get you in trouble.  Come to think of it, it’s just like being a DBA.

SQL Server 2008 Data Compression: No Inheritance Either

Microsoft SQL Server 2008’s Data Compression feature lets you compress objects – not just tables, but individual indexes.  This compression does incur a little extra CPU power to handle the compression, but that extra overhead is more than offset by increased IO performance.  Generally speaking, the database server is sitting around waiting on disk subsystems.  Adding a little CPU work while dramatically reducing IO needs results in faster query return times.  You need to test compression to see if it works well in your environment, because it may not work well in heavy-insert databases.

Today, though, I’m going to focus on the dark side of compression: a complete lack of inheritance.

When you compress tables and indexes, it’s a one-time action.  You’re only taking care of what exists today.  If someone (or even you) turns around and creates an index on that same table tomorrow, it won’t be compressed by default.  Whoever creates the index has to make sure that it’s compressed, and there’s nothing in SQL Server Management Studio that will hint to them that other parts of that same object are compressed.

To make matters worse, your development, QA and production environments might all have different compression settings, and you’d never notice it at a glance.  Compression is transparent to applications, so your developers won’t know why one environment performs much differently than another even though they have the same hardware, same indexes, same statistics.

Implementing compression is a multi-step process:

  • Figure out what objects you should compress
  • Plan to handle all of your environments (dev, QA, production)
  • Compress them during a low-activity window
  • Regularly patrol your environments checking for added objects that weren’t compressed
  • Keep your environments in sync

If you don’t stay on top of all of these, you’ll need Valium too.

* – Yes, about that picture.  No, it’s not mine.  Yes, it’s licensed with Creative Commons.  No, it wasn’t even the worst jungle juice picture I could find licensed with Creative Commons.  Yes, I too am amazed that people upload their party pictures to Flickr, let alone license them with Creative Commons.

Tom LaRock on Blogging

Monday, August 31st, 2009

Tom LaRock (BlogTwitter) is a man of many hats: he’s a SQLServerPedia Editor, a PASS Board of Directors member, a Microsoft SQL Server MVP, and much more.Brent Ozar  interviewed him at Quest’s headquarters about how he got started blogging and what he gets out of it.

Get the Flash Player to see the wordTube Media Player.

Subscribing or Downloading the Podcast

If you have an MP3 player or a portable video player and you want to download our videos automatically, you can subscribe to our podcast feeds here:

You can also download this video to watch it later:

Want to Guest Podcast at SQLServerPedia?

We’d love to host your guest podcasts! Email Editor-in-Chief Brent.Ozar@Quest.com and he’ll help you get started with recording your own presentation. We’ll host them at SQLServerPedia, and you can also host them on your own site. We don’t need exclusive rights to the podcast.

Scripting Server Side Traces

Monday, August 31st, 2009

If you’ve never used SQL Server Profiler, you truly don’t know what your missing. And that statement has an intentional double meaning. You don’t know what a powerfully informative tool you’re missing, and you really don’t know what’s going on inside your SQL Server box either. Once you try it and figure it out, it’ll be like getting a new birthday present. Or finding $20 in your pants pocket when you wash your jeans.

Using SQL Profiler

There are plenty of tutorials online to help get you started using SQL Profiler. Books Online is a great place to start. It provides a good introduction as well as showing you the permissions required to run SQL Server Profiler. Brad McGehee, who probably knows as much about it as anyone, has written a book dedicated to the subject; it’s available as a free pdf download. And SQLServerPedia and JumpStartTv both have video tutorials so you can watch someone else show you how it’s done.

But Performance was bad before…

SQL Server Profiler can be great on development and test systems, but it only takes a time or two of running SQL Server Profiler on a production server before you learn the hard way that the SQL Server Profiler can have a significantly negative impact on performance. When you fire up the trusty tool, it doesn’t take long before your phone starts ringing. If you thought performance was an issue before, you ain’t seen nothin’ yet.

A disgruntled user calling

In speaking engagements on identifying performance bottlenecks, I often refer to Linchi Shea’s eye-opening work on quantifying the performance impacts fo SQL Server Profiler. His blog post on the subject is clear and convincing. It puts numbers on something that we knew anecdotally.

But what is really noteworthy in the blog is the impact, or really the lack of impact, that Server Side Traces have on performance. I won’t repeat his proof or findings here; go read it for yourself. But suffice it to say, you should use Server Side Traces and not SQL Server Profiler on production systems. Server Side Traces are very similar to SQL Server Profiler traces only without the overhead.

Configuring Server Side Traces

Okay, okay, Server Side Traces are the way to go. But have you seen the scripts required to set one of those up? They are ugly! Just look at the Books Online example!

Here’s another example. The script below creates a really simple Server Side Trace. It only captures a few events (Stored Procedures – RPC:Completed, SP:StmtCompleted and TSQL – SQL:BatchCompleted) for the AdventureWorks and AdventureWorksDW databases, saving the output to a local drive. It’s not quite Assembler, but it’s not that fun to write either.

– Create a Queue
declare @rc int
declare @TraceID int
declare @maxfilesize bigint
set @maxfilesize = 100

– Create the trace
exec @rc = sp_trace_create @TraceID output, 0, N’E:\demo\demotrace’, @maxfilesize, NULL
if (@rc != 0) goto error

– Set the events
declare @on bit
set @on = 1
exec sp_trace_setevent @TraceID, 10, 15, @on
exec sp_trace_setevent @TraceID, 10, 31, @on
exec sp_trace_setevent @TraceID, 10, 8, @on
exec sp_trace_setevent @TraceID, 10, 48, @on
exec sp_trace_setevent @TraceID, 10, 1, @on
exec sp_trace_setevent @TraceID, 10, 10, @on
exec sp_trace_setevent @TraceID, 10, 34, @on
exec sp_trace_setevent @TraceID, 10, 3, @on
exec sp_trace_setevent @TraceID, 10, 11, @on
exec sp_trace_setevent @TraceID, 10, 35, @on
exec sp_trace_setevent @TraceID, 10, 12, @on
exec sp_trace_setevent @TraceID, 10, 13, @on
exec sp_trace_setevent @TraceID, 10, 14, @on
exec sp_trace_setevent @TraceID, 45, 8, @on
exec sp_trace_setevent @TraceID, 45, 48, @on
exec sp_trace_setevent @TraceID, 45, 1, @on
exec sp_trace_setevent @TraceID, 45, 10, @on
exec sp_trace_setevent @TraceID, 45, 34, @on
exec sp_trace_setevent @TraceID, 45, 3, @on
exec sp_trace_setevent @TraceID, 45, 11, @on
exec sp_trace_setevent @TraceID, 45, 35, @on
exec sp_trace_setevent @TraceID, 45, 12, @on
exec sp_trace_setevent @TraceID, 45, 28, @on
exec sp_trace_setevent @TraceID, 45, 13, @on
exec sp_trace_setevent @TraceID, 45, 14, @on
exec sp_trace_setevent @TraceID, 45, 15, @on
exec sp_trace_setevent @TraceID, 12, 15, @on
exec sp_trace_setevent @TraceID, 12, 31, @on
exec sp_trace_setevent @TraceID, 12, 8, @on
exec sp_trace_setevent @TraceID, 12, 48, @on
exec sp_trace_setevent @TraceID, 12, 1, @on
exec sp_trace_setevent @TraceID, 12, 10, @on
exec sp_trace_setevent @TraceID, 12, 14, @on
exec sp_trace_setevent @TraceID, 12, 3, @on
exec sp_trace_setevent @TraceID, 12, 11, @on
exec sp_trace_setevent @TraceID, 12, 35, @on
exec sp_trace_setevent @TraceID, 12, 12, @on
exec sp_trace_setevent @TraceID, 12, 13, @on

– Set the Filters
declare @intfilter int
declare @bigintfilter bigint

set @intfilter = 6
exec sp_trace_setfilter @TraceID, 3, 0, 0, @intfilter

set @intfilter = 5
exec sp_trace_setfilter @TraceID, 3, 1, 0, @intfilter

– display trace id for future references
select TraceID=@TraceID
goto finish

error:
select ErrorCode=@rc

finish:
go

Here is a list event ids and filter options for Server Side Traces. Fortunately, we don’t have to create all this by hand. We can return to our beloved SQL Server Profiler and have it do the heavy lifting for us.

Turning SQL Server Profiler traces into Server Side Traces

Once you’ve configure a trace in SQL Server Profiler, you can export the trace definition. Click on File | Export | Script Trace Definition and select the version of SQL Server you have. I’m using the version of SQL Server Profiler that came with SQL Server 2008 and I have the option to script the trace definition for SQL Server 2005-2008. If you’re using SQL Server 2000, you have your own special option for their trace definition.

To see the fruits of our efforts, let’s look at the script file to see the trace definition in T-SQL. As you can see, it’s all there.

/****************************************************/
/* Created by: SQL Server 2008 Profiler             */
/* Date: 08/27/2009  08:59:12 AM         */
/****************************************************/

– Create a Queue
declare @rc int
declare @TraceID int
declare @maxfilesize bigint
set @maxfilesize = 500

– Please replace the text InsertFileNameHere, with an appropriate
– filename prefixed by a path, e.g., c:\MyFolder\MyTrace. The .trc extension
– will be appended to the filename automatically. If you are writing from
– remote server to local drive, please use UNC path and make sure server has
– write access to your network share

exec @rc = sp_trace_create @TraceID output, 0, N’E:\demo\demotrace2′, @maxfilesize, NULL
if (@rc != 0) goto error

– Client side File and Table cannot be scripted

– Set the events
declare @on bit
set @on = 1
exec sp_trace_setevent @TraceID, 27, 7, @on
exec sp_trace_setevent @TraceID, 27, 15, @on
exec sp_trace_setevent @TraceID, 27, 55, @on
exec sp_trace_setevent @TraceID, 27, 8, @on
exec sp_trace_setevent @TraceID, 27, 32, @on
exec sp_trace_setevent @TraceID, 27, 56, @on
exec sp_trace_setevent @TraceID, 27, 64, @on
exec sp_trace_setevent @TraceID, 27, 1, @on
exec sp_trace_setevent @TraceID, 27, 9, @on
exec sp_trace_setevent @TraceID, 27, 41, @on
exec sp_trace_setevent @TraceID, 27, 49, @on
exec sp_trace_setevent @TraceID, 27, 57, @on
exec sp_trace_setevent @TraceID, 27, 2, @on
exec sp_trace_setevent @TraceID, 27, 10, @on
exec sp_trace_setevent @TraceID, 27, 26, @on
exec sp_trace_setevent @TraceID, 27, 58, @on
exec sp_trace_setevent @TraceID, 27, 66, @on
exec sp_trace_setevent @TraceID, 27, 3, @on
exec sp_trace_setevent @TraceID, 27, 11, @on
exec sp_trace_setevent @TraceID, 27, 35, @on
exec sp_trace_setevent @TraceID, 27, 51, @on
exec sp_trace_setevent @TraceID, 27, 4, @on
exec sp_trace_setevent @TraceID, 27, 12, @on
exec sp_trace_setevent @TraceID, 27, 52, @on
exec sp_trace_setevent @TraceID, 27, 60, @on
exec sp_trace_setevent @TraceID, 27, 13, @on
exec sp_trace_setevent @TraceID, 27, 6, @on
exec sp_trace_setevent @TraceID, 27, 14, @on
exec sp_trace_setevent @TraceID, 27, 22, @on
exec sp_trace_setevent @TraceID, 189, 7, @on
exec sp_trace_setevent @TraceID, 189, 15, @on
exec sp_trace_setevent @TraceID, 189, 55, @on
exec sp_trace_setevent @TraceID, 189, 8, @on
exec sp_trace_setevent @TraceID, 189, 32, @on
exec sp_trace_setevent @TraceID, 189, 56, @on
exec sp_trace_setevent @TraceID, 189, 64, @on
exec sp_trace_setevent @TraceID, 189, 1, @on
exec sp_trace_setevent @TraceID, 189, 9, @on
exec sp_trace_setevent @TraceID, 189, 41, @on
exec sp_trace_setevent @TraceID, 189, 49, @on
exec sp_trace_setevent @TraceID, 189, 57, @on
exec sp_trace_setevent @TraceID, 189, 2, @on
exec sp_trace_setevent @TraceID, 189, 10, @on
exec sp_trace_setevent @TraceID, 189, 26, @on
exec sp_trace_setevent @TraceID, 189, 58, @on
exec sp_trace_setevent @TraceID, 189, 66, @on
exec sp_trace_setevent @TraceID, 189, 3, @on
exec sp_trace_setevent @TraceID, 189, 11, @on
exec sp_trace_setevent @TraceID, 189, 35, @on
exec sp_trace_setevent @TraceID, 189, 51, @on
exec sp_trace_setevent @TraceID, 189, 4, @on
exec sp_trace_setevent @TraceID, 189, 12, @on
exec sp_trace_setevent @TraceID, 189, 52, @on
exec sp_trace_setevent @TraceID, 189, 60, @on
exec sp_trace_setevent @TraceID, 189, 13, @on
exec sp_trace_setevent @TraceID, 189, 6, @on
exec sp_trace_setevent @TraceID, 189, 14, @on
exec sp_trace_setevent @TraceID, 189, 22, @on
exec sp_trace_setevent @TraceID, 122, 7, @on
exec sp_trace_setevent @TraceID, 122, 8, @on
exec sp_trace_setevent @TraceID, 122, 64, @on
exec sp_trace_setevent @TraceID, 122, 1, @on
exec sp_trace_setevent @TraceID, 122, 9, @on
exec sp_trace_setevent @TraceID, 122, 25, @on
exec sp_trace_setevent @TraceID, 122, 41, @on
exec sp_trace_setevent @TraceID, 122, 49, @on
exec sp_trace_setevent @TraceID, 122, 2, @on
exec sp_trace_setevent @TraceID, 122, 10, @on
exec sp_trace_setevent @TraceID, 122, 14, @on
exec sp_trace_setevent @TraceID, 122, 22, @on
exec sp_trace_setevent @TraceID, 122, 26, @on
exec sp_trace_setevent @TraceID, 122, 34, @on
exec sp_trace_setevent @TraceID, 122, 50, @on
exec sp_trace_setevent @TraceID, 122, 66, @on
exec sp_trace_setevent @TraceID, 122, 3, @on
exec sp_trace_setevent @TraceID, 122, 11, @on
exec sp_trace_setevent @TraceID, 122, 35, @on
exec sp_trace_setevent @TraceID, 122, 51, @on
exec sp_trace_setevent @TraceID, 122, 4, @on
exec sp_trace_setevent @TraceID, 122, 12, @on
exec sp_trace_setevent @TraceID, 122, 28, @on
exec sp_trace_setevent @TraceID, 122, 60, @on
exec sp_trace_setevent @TraceID, 122, 5, @on
exec sp_trace_setevent @TraceID, 122, 29, @on
exec sp_trace_setevent @TraceID, 10, 15, @on
exec sp_trace_setevent @TraceID, 10, 31, @on
exec sp_trace_setevent @TraceID, 10, 8, @on
exec sp_trace_setevent @TraceID, 10, 48, @on
exec sp_trace_setevent @TraceID, 10, 1, @on
exec sp_trace_setevent @TraceID, 10, 10, @on
exec sp_trace_setevent @TraceID, 10, 34, @on
exec sp_trace_setevent @TraceID, 10, 3, @on
exec sp_trace_setevent @TraceID, 10, 11, @on
exec sp_trace_setevent @TraceID, 10, 35, @on
exec sp_trace_setevent @TraceID, 10, 12, @on
exec sp_trace_setevent @TraceID, 10, 13, @on
exec sp_trace_setevent @TraceID, 10, 14, @on
exec sp_trace_setevent @TraceID, 45, 8, @on
exec sp_trace_setevent @TraceID, 45, 48, @on
exec sp_trace_setevent @TraceID, 45, 1, @on
exec sp_trace_setevent @TraceID, 45, 10, @on
exec sp_trace_setevent @TraceID, 45, 34, @on
exec sp_trace_setevent @TraceID, 45, 3, @on
exec sp_trace_setevent @TraceID, 45, 11, @on
exec sp_trace_setevent @TraceID, 45, 35, @on
exec sp_trace_setevent @TraceID, 45, 12, @on
exec sp_trace_setevent @TraceID, 45, 28, @on
exec sp_trace_setevent @TraceID, 45, 13, @on
exec sp_trace_setevent @TraceID, 45, 14, @on
exec sp_trace_setevent @TraceID, 45, 15, @on
exec sp_trace_setevent @TraceID, 12, 15, @on
exec sp_trace_setevent @TraceID, 12, 31, @on
exec sp_trace_setevent @TraceID, 12, 8, @on
exec sp_trace_setevent @TraceID, 12, 48, @on
exec sp_trace_setevent @TraceID, 12, 1, @on
exec sp_trace_setevent @TraceID, 12, 10, @on
exec sp_trace_setevent @TraceID, 12, 14, @on
exec sp_trace_setevent @TraceID, 12, 3, @on
exec sp_trace_setevent @TraceID, 12, 11, @on
exec sp_trace_setevent @TraceID, 12, 35, @on
exec sp_trace_setevent @TraceID, 12, 12, @on
exec sp_trace_setevent @TraceID, 12, 13, @on

– Set the Filters
declare @intfilter int
declare @bigintfilter bigint

set @intfilter = 6
exec sp_trace_setfilter @TraceID, 3, 0, 0, @intfilter

set @intfilter = 5
exec sp_trace_setfilter @TraceID, 3, 1, 0, @intfilter

– Set the trace status to start
exec sp_trace_setstatus @TraceID, 1

– display trace id for future references
select TraceID=@TraceID
goto finish

error:
select ErrorCode=@rc

finish:
go

This looks remarkably similiar to the one I showed earlier in the post. I used SQL Server Profiler to create that one for me, too.

Next Up: running and scheduling Server Side Traces

Whew! This blog has almost exceeded our collective attention spans, so we’ll stop here for now. But in the next post, I’ll show you how to run Server Side Traces and even schedule them to run automatically so you can capture benchmarking data.

Until then, I’d love to hear your experiences with SQL Server Profiler and Server Side Traces. Got some good scripts that you don’t mind sharing? Post or link to them in the comments section below.

Joe

Scripting Server Side Traces

Monday, August 31st, 2009

If you’ve never used SQL Server Profiler, you truly don’t know what your missing. And that statement has an intentional double meaning. You don’t know what a powerfully informative tool you’re missing, and you really don’t know what’s going on inside your SQL Server box either. Once you try it and figure it out, it’ll be like getting a new birthday present. Or finding $20 in your pants pocket when you wash your jeans.

Using SQL Profiler

There are plenty of tutorials online to help get you started using SQL Profiler. Books Online is a great place to start. It provides a good introduction as well as showing you the permissions required to run SQL Server Profiler. Brad McGehee, who probably knows as much about it as anyone, has written a book dedicated to the subject; it’s available as a free pdf download. And SQLServerPedia and JumpStartTv both have video tutorials so you can watch someone else show you how it’s done.

But Performance was bad before…

SQL Server Profiler can be great on development and test systems, but it only takes a time or two of running SQL Server Profiler on a production server before you learn the hard way that the SQL Server Profiler can have a significantly negative impact on performance. When you fire up the trusty tool, it doesn’t take long before your phone starts ringing. If you thought performance was an issue before, you ain’t seen nothin’ yet.

A disgruntled user calling

In speaking engagements on identifying performance bottlenecks, I often refer to Linchi Shea’s eye-opening work on quantifying the performance impacts fo SQL Server Profiler. His blog post on the subject is clear and convincing. It puts numbers on something that we knew anecdotally.

But what is really noteworthy in the blog is the impact, or really the lack of impact, that Server Side Traces have on performance. I won’t repeat his proof or findings here; go read it for yourself. But suffice it to say, you should use Server Side Traces and not SQL Server Profiler on production systems. Server Side Traces are very similar to SQL Server Profiler traces only without the overhead.

Configuring Server Side Traces

Okay, okay, Server Side Traces are the way to go. But have you seen the scripts required to set one of those up? They are ugly! Just look at the Books Online example!

Here’s another example. The script below creates a really simple Server Side Trace. It only captures a few events (Stored Procedures – RPC:Completed, SP:StmtCompleted and TSQL – SQL:BatchCompleted) for the AdventureWorks and AdventureWorksDW databases, saving the output to a local drive. It’s not quite Assembler, but it’s not that fun to write either.

– Create a Queue
declare @rc int
declare @TraceID int
declare @maxfilesize bigint
set @maxfilesize = 100

– Create the trace
exec @rc = sp_trace_create @TraceID output, 0, N’E:\demo\demotrace’, @maxfilesize, NULL
if (@rc != 0) goto error

– Set the events
declare @on bit
set @on = 1
exec sp_trace_setevent @TraceID, 10, 15, @on
exec sp_trace_setevent @TraceID, 10, 31, @on
exec sp_trace_setevent @TraceID, 10, 8, @on
exec sp_trace_setevent @TraceID, 10, 48, @on
exec sp_trace_setevent @TraceID, 10, 1, @on
exec sp_trace_setevent @TraceID, 10, 10, @on
exec sp_trace_setevent @TraceID, 10, 34, @on
exec sp_trace_setevent @TraceID, 10, 3, @on
exec sp_trace_setevent @TraceID, 10, 11, @on
exec sp_trace_setevent @TraceID, 10, 35, @on
exec sp_trace_setevent @TraceID, 10, 12, @on
exec sp_trace_setevent @TraceID, 10, 13, @on
exec sp_trace_setevent @TraceID, 10, 14, @on
exec sp_trace_setevent @TraceID, 45, 8, @on
exec sp_trace_setevent @TraceID, 45, 48, @on
exec sp_trace_setevent @TraceID, 45, 1, @on
exec sp_trace_setevent @TraceID, 45, 10, @on
exec sp_trace_setevent @TraceID, 45, 34, @on
exec sp_trace_setevent @TraceID, 45, 3, @on
exec sp_trace_setevent @TraceID, 45, 11, @on
exec sp_trace_setevent @TraceID, 45, 35, @on
exec sp_trace_setevent @TraceID, 45, 12, @on
exec sp_trace_setevent @TraceID, 45, 28, @on
exec sp_trace_setevent @TraceID, 45, 13, @on
exec sp_trace_setevent @TraceID, 45, 14, @on
exec sp_trace_setevent @TraceID, 45, 15, @on
exec sp_trace_setevent @TraceID, 12, 15, @on
exec sp_trace_setevent @TraceID, 12, 31, @on
exec sp_trace_setevent @TraceID, 12, 8, @on
exec sp_trace_setevent @TraceID, 12, 48, @on
exec sp_trace_setevent @TraceID, 12, 1, @on
exec sp_trace_setevent @TraceID, 12, 10, @on
exec sp_trace_setevent @TraceID, 12, 14, @on
exec sp_trace_setevent @TraceID, 12, 3, @on
exec sp_trace_setevent @TraceID, 12, 11, @on
exec sp_trace_setevent @TraceID, 12, 35, @on
exec sp_trace_setevent @TraceID, 12, 12, @on
exec sp_trace_setevent @TraceID, 12, 13, @on

– Set the Filters
declare @intfilter int
declare @bigintfilter bigint

set @intfilter = 6
exec sp_trace_setfilter @TraceID, 3, 0, 0, @intfilter

set @intfilter = 5
exec sp_trace_setfilter @TraceID, 3, 1, 0, @intfilter

– display trace id for future references
select TraceID=@TraceID
goto finish

error:
select ErrorCode=@rc

finish:
go

Here is a list event ids and filter options for Server Side Traces. Fortunately, we don’t have to create all this by hand. We can return to our beloved SQL Server Profiler and have it do the heavy lifting for us.

Turning SQL Server Profiler traces into Server Side Traces

Once you’ve configure a trace in SQL Server Profiler, you can export the trace definition. Click on File | Export | Script Trace Definition and select the version of SQL Server you have. I’m using the version of SQL Server Profiler that came with SQL Server 2008 and I have the option to script the trace definition for SQL Server 2005-2008. If you’re using SQL Server 2000, you have your own special option for their trace definition.

To see the fruits of our efforts, let’s look at the script file to see the trace definition in T-SQL. As you can see, it’s all there.

/****************************************************/
/* Created by: SQL Server 2008 Profiler             */
/* Date: 08/27/2009  08:59:12 AM         */
/****************************************************/

– Create a Queue
declare @rc int
declare @TraceID int
declare @maxfilesize bigint
set @maxfilesize = 500

– Please replace the text InsertFileNameHere, with an appropriate
– filename prefixed by a path, e.g., c:\MyFolder\MyTrace. The .trc extension
– will be appended to the filename automatically. If you are writing from
– remote server to local drive, please use UNC path and make sure server has
– write access to your network share

exec @rc = sp_trace_create @TraceID output, 0, N’E:\demo\demotrace2′, @maxfilesize, NULL
if (@rc != 0) goto error

– Client side File and Table cannot be scripted

– Set the events
declare @on bit
set @on = 1
exec sp_trace_setevent @TraceID, 27, 7, @on
exec sp_trace_setevent @TraceID, 27, 15, @on
exec sp_trace_setevent @TraceID, 27, 55, @on
exec sp_trace_setevent @TraceID, 27, 8, @on
exec sp_trace_setevent @TraceID, 27, 32, @on
exec sp_trace_setevent @TraceID, 27, 56, @on
exec sp_trace_setevent @TraceID, 27, 64, @on
exec sp_trace_setevent @TraceID, 27, 1, @on
exec sp_trace_setevent @TraceID, 27, 9, @on
exec sp_trace_setevent @TraceID, 27, 41, @on
exec sp_trace_setevent @TraceID, 27, 49, @on
exec sp_trace_setevent @TraceID, 27, 57, @on
exec sp_trace_setevent @TraceID, 27, 2, @on
exec sp_trace_setevent @TraceID, 27, 10, @on
exec sp_trace_setevent @TraceID, 27, 26, @on
exec sp_trace_setevent @TraceID, 27, 58, @on
exec sp_trace_setevent @TraceID, 27, 66, @on
exec sp_trace_setevent @TraceID, 27, 3, @on
exec sp_trace_setevent @TraceID, 27, 11, @on
exec sp_trace_setevent @TraceID, 27, 35, @on
exec sp_trace_setevent @TraceID, 27, 51, @on
exec sp_trace_setevent @TraceID, 27, 4, @on
exec sp_trace_setevent @TraceID, 27, 12, @on
exec sp_trace_setevent @TraceID, 27, 52, @on
exec sp_trace_setevent @TraceID, 27, 60, @on
exec sp_trace_setevent @TraceID, 27, 13, @on
exec sp_trace_setevent @TraceID, 27, 6, @on
exec sp_trace_setevent @TraceID, 27, 14, @on
exec sp_trace_setevent @TraceID, 27, 22, @on
exec sp_trace_setevent @TraceID, 189, 7, @on
exec sp_trace_setevent @TraceID, 189, 15, @on
exec sp_trace_setevent @TraceID, 189, 55, @on
exec sp_trace_setevent @TraceID, 189, 8, @on
exec sp_trace_setevent @TraceID, 189, 32, @on
exec sp_trace_setevent @TraceID, 189, 56, @on
exec sp_trace_setevent @TraceID, 189, 64, @on
exec sp_trace_setevent @TraceID, 189, 1, @on
exec sp_trace_setevent @TraceID, 189, 9, @on
exec sp_trace_setevent @TraceID, 189, 41, @on
exec sp_trace_setevent @TraceID, 189, 49, @on
exec sp_trace_setevent @TraceID, 189, 57, @on
exec sp_trace_setevent @TraceID, 189, 2, @on
exec sp_trace_setevent @TraceID, 189, 10, @on
exec sp_trace_setevent @TraceID, 189, 26, @on
exec sp_trace_setevent @TraceID, 189, 58, @on
exec sp_trace_setevent @TraceID, 189, 66, @on
exec sp_trace_setevent @TraceID, 189, 3, @on
exec sp_trace_setevent @TraceID, 189, 11, @on
exec sp_trace_setevent @TraceID, 189, 35, @on
exec sp_trace_setevent @TraceID, 189, 51, @on
exec sp_trace_setevent @TraceID, 189, 4, @on
exec sp_trace_setevent @TraceID, 189, 12, @on
exec sp_trace_setevent @TraceID, 189, 52, @on
exec sp_trace_setevent @TraceID, 189, 60, @on
exec sp_trace_setevent @TraceID, 189, 13, @on
exec sp_trace_setevent @TraceID, 189, 6, @on
exec sp_trace_setevent @TraceID, 189, 14, @on
exec sp_trace_setevent @TraceID, 189, 22, @on
exec sp_trace_setevent @TraceID, 122, 7, @on
exec sp_trace_setevent @TraceID, 122, 8, @on
exec sp_trace_setevent @TraceID, 122, 64, @on
exec sp_trace_setevent @TraceID, 122, 1, @on
exec sp_trace_setevent @TraceID, 122, 9, @on
exec sp_trace_setevent @TraceID, 122, 25, @on
exec sp_trace_setevent @TraceID, 122, 41, @on
exec sp_trace_setevent @TraceID, 122, 49, @on
exec sp_trace_setevent @TraceID, 122, 2, @on
exec sp_trace_setevent @TraceID, 122, 10, @on
exec sp_trace_setevent @TraceID, 122, 14, @on
exec sp_trace_setevent @TraceID, 122, 22, @on
exec sp_trace_setevent @TraceID, 122, 26, @on
exec sp_trace_setevent @TraceID, 122, 34, @on
exec sp_trace_setevent @TraceID, 122, 50, @on
exec sp_trace_setevent @TraceID, 122, 66, @on
exec sp_trace_setevent @TraceID, 122, 3, @on
exec sp_trace_setevent @TraceID, 122, 11, @on
exec sp_trace_setevent @TraceID, 122, 35, @on
exec sp_trace_setevent @TraceID, 122, 51, @on
exec sp_trace_setevent @TraceID, 122, 4, @on
exec sp_trace_setevent @TraceID, 122, 12, @on
exec sp_trace_setevent @TraceID, 122, 28, @on
exec sp_trace_setevent @TraceID, 122, 60, @on
exec sp_trace_setevent @TraceID, 122, 5, @on
exec sp_trace_setevent @TraceID, 122, 29, @on
exec sp_trace_setevent @TraceID, 10, 15, @on
exec sp_trace_setevent @TraceID, 10, 31, @on
exec sp_trace_setevent @TraceID, 10, 8, @on
exec sp_trace_setevent @TraceID, 10, 48, @on
exec sp_trace_setevent @TraceID, 10, 1, @on
exec sp_trace_setevent @TraceID, 10, 10, @on
exec sp_trace_setevent @TraceID, 10, 34, @on
exec sp_trace_setevent @TraceID, 10, 3, @on
exec sp_trace_setevent @TraceID, 10, 11, @on
exec sp_trace_setevent @TraceID, 10, 35, @on
exec sp_trace_setevent @TraceID, 10, 12, @on
exec sp_trace_setevent @TraceID, 10, 13, @on
exec sp_trace_setevent @TraceID, 10, 14, @on
exec sp_trace_setevent @TraceID, 45, 8, @on
exec sp_trace_setevent @TraceID, 45, 48, @on
exec sp_trace_setevent @TraceID, 45, 1, @on
exec sp_trace_setevent @TraceID, 45, 10, @on
exec sp_trace_setevent @TraceID, 45, 34, @on
exec sp_trace_setevent @TraceID, 45, 3, @on
exec sp_trace_setevent @TraceID, 45, 11, @on
exec sp_trace_setevent @TraceID, 45, 35, @on
exec sp_trace_setevent @TraceID, 45, 12, @on
exec sp_trace_setevent @TraceID, 45, 28, @on
exec sp_trace_setevent @TraceID, 45, 13, @on
exec sp_trace_setevent @TraceID, 45, 14, @on
exec sp_trace_setevent @TraceID, 45, 15, @on
exec sp_trace_setevent @TraceID, 12, 15, @on
exec sp_trace_setevent @TraceID, 12, 31, @on
exec sp_trace_setevent @TraceID, 12, 8, @on
exec sp_trace_setevent @TraceID, 12, 48, @on
exec sp_trace_setevent @TraceID, 12, 1, @on
exec sp_trace_setevent @TraceID, 12, 10, @on
exec sp_trace_setevent @TraceID, 12, 14, @on
exec sp_trace_setevent @TraceID, 12, 3, @on
exec sp_trace_setevent @TraceID, 12, 11, @on
exec sp_trace_setevent @TraceID, 12, 35, @on
exec sp_trace_setevent @TraceID, 12, 12, @on
exec sp_trace_setevent @TraceID, 12, 13, @on

– Set the Filters
declare @intfilter int
declare @bigintfilter bigint

set @intfilter = 6
exec sp_trace_setfilter @TraceID, 3, 0, 0, @intfilter

set @intfilter = 5
exec sp_trace_setfilter @TraceID, 3, 1, 0, @intfilter

– Set the trace status to start
exec sp_trace_setstatus @TraceID, 1

– display trace id for future references
select TraceID=@TraceID
goto finish

error:
select ErrorCode=@rc

finish:
go

This looks remarkably similiar to the one I showed earlier in the post. I used SQL Server Profiler to create that one for me, too.

Next Up: running and scheduling Server Side Traces

Whew! This blog has almost exceeded our collective attention spans, so we’ll stop here for now. But in the next post, I’ll show you how to run Server Side Traces and even schedule them to run automatically so you can capture benchmarking data.

Until then, I’d love to hear your experiences with SQL Server Profiler and Server Side Traces. Got some good scripts that you don’t mind sharing? Post or link to them in the comments section below.

Joe

EDMPASS – September Meeting w/Brent Ozar

Monday, August 31st, 2009

On September 30th 2009 the Edmonton Chapter of PASS is having it’s next meeting. Details below:

http://www.eventbrite.com/event/421182769

Please be sure to not only click the “Add to my calendar” but also the “Register” button so that we can plan accordingly for food and drinks. 

Date:  Sept 30th 2009
Time: 5:00 pm – 7:00 pm
Location: Stanley A. Milner library
Map: 7 Sir Winston Churchill Square
Meeting Room: 6th Floor – Room 7
Speaker: Brent Ozar
Topic: Hit The Ground Running with SQL Server 2008

Session Abstract: SQL 2008 has a lot of new features to help make database administration easier – but who has the time to learn ‘em all? Brent will get you started fast with the features that give you the most bang for the buck. Attendees will learn how to use these new 2008 features:

  • Central Management Server
  • Policy-Based Management
  • Group Query Execute
  • Resource Governor

Presenter Information:  Brent Ozar

Brent is a SQL Server Domain Expert with Quest Software. Brent has a decade of broad IT experience, performing systems administration and project management before moving into database administration. In his current role, Brent specializes in performance tuning, disaster recovery and automating SQL Server management. Previously, Brent spent 2 years at Southern Wine & Spirits, a Miami-based wine & spirits distributor.

Brent has experience conducting training sessions, has written several technical articles, and blogs prolifically at http://www.BrentOzar.com. He’s currently writing SQL Server Internals and Troubleshooting for Wiley/Wrox along with Christian Bolton, and Justin Langford.  He’s Editor-in-Chief at SQLServerPedia.com, where he also records video podcasts.

Agenda:
5:00 pm – Pizza and Socializing
5:30 pm – Sponsor Presentation
5:45 pm – Feature Presentation
7:00 pm – Wrap Up and Draws

If you haven’t signed up already at EDMPASS.com please do so now to receive meeting notifications, news, and updates from EDMPASS.

Hope to see you there.

Enjoy!!

Post to Twitter Post to Delicious Post to Digg Post to StumbleUpon

Dr. Strangecation (or how I stopped worrying and learned how to love the PTO)

Monday, August 31st, 2009

It’s not often that I take an extended period of time off.  But seeing almost 300 hours of PTO banked on my last paycheck was a wake-up call to get away and detoxify myself from my professional responsibilities.  We can not uphold our commitments to our customers without first caring for our personal fulfillment.  Families, friends, mental and physical exercise, those quiet spaces in between everything else; those are all critical components of keeping us sane enough to cope with the mental (and sometimes physical) exertion we in Information Technology must expend in order to meet the requirements of our positions.  I realize now that I forgot about that though.  Work became life.  This is a risk of telecommuting.  I’ve often found myself extolling the benefits of remote working and stressing that there are also risks inherent in doing so - this being one. 

I learned a few things on my week away.  None of them having to do with SQL Server, databases, technology, or anything else I normally focus my attention on.  I forgot there is an entire other world out there:

  • I’ve learned that if you want fast food in South Dakota you better get it within the city limits of Rapid City or Sioux Falls.
  • I’ve learned that a civilized people will build a monument to anything from corn to mythical jackrabbit/antelope hybrids.
  • I’ve learned that bison and tofu are both alternatives to beef.  However, tofu has never been described as “gamey”.
  • I’ve learned that you never, ever, drive through Chicago unless you absolutely have to.  Oprah has one helluva entourage and they all have cars.
  • I’ve learned that there is a store in Wall, SD that will sell anything - an there is at least one purple stretch pant clad grandma who is there to buy it.
  • I’ve learned that Hollywood depictions of Minnesota and Wisconsin are way off the mark; those states are not that glamorous.
  • I’ve learned that every exit along Interstate 90 in Wisconsin will provide you with a cheese shop.
  • I’ve learned that all those newspapers no one reads are being used as toilet paper in hotels servicing the states of South Dakota, and Iowa.
  • I’ve learned that despite all those things you will have fun by simply experiencing the unfamiliar with those you love.

So, as the summer rapidly winds down I wanted to take a moment to remind you that there is still time to take a few days off; step away from the keyboard, back off the mouse, flee from the flourescents and go outdoors.  Just be sure to carry your own toilet paper and a well-stocked cooler.

Your family and your customers will thank you.

PASS Is For Everyone

Sunday, August 30th, 2009

Last week I received an email from @wnylibrarian:

Hi Thomas,

I’ve been thinking about registering with PASS, but I just feel slightly uneasy doing so because I’m what you would refer to as an “accidental” DBA. I hold no certifications, but have a Master’s Degree in Library Science. I worry sometimes about what I can contribute. My work with SQL Server was based on necessity. We were getting a vendor product that used SQL Server a few years ago. I had to learn on the job, and I like to think I’ve done pretty well. Yet when I read your blog and others I know there’s so much I don’t know. Do you have any thoughts or suggestions? Are there other paraprofessionals involved with PASS? I’m curious about it, but also wonder if it is the correct fit for a technical librarian.

Thank you in advance for any insight you may provide.

My first thought was “Absolutely! Of course you should come to the Summit, because the Summit is for everyone.” And then I had a much more sobering second thought: “Did I leave the bacon in the oven?”, which was followed by “Wait a minute, here is one of those ‘accidental’ DBA’s wondering if they belong at the PASS Summit. How many more people like this are there?”

Now I have no idea how many ‘accidental’ DBA’s there are, or how to find them. But I wish I could reach out to all of them and spend a few minutes explaining that PASS is for people of all technical abilities and interests. You want to learn about BI? We got that. You want to learn about multi-server management? We got some of that, too. You want to network with a few thousand of your closest friends? No problem. You want database design theory, learn about the storage engine, or complain directly to Microsoft about how your Connect items get closed without any comments…we got all of that.

We all start somewhere in life and walk our own path and along the way we hope to meet others and share our life experiences. When you go to PASS, that is what you get: the ultimate in sharing of experiences for SQL Server. It does not matter where you are along in your path, you will find others just like yourself. And then you get to journey together, which is like being injected with liquid awesome. Or bacon, you get your choice.

I am one person, I cannot possibly know everything there is to know about SQL Server. But as a whole the PASS community does know everything. Don’t believe me? Come to PASS with your questions and I promise to personally help you find the answers. That’s right, you read that correctly:

At the PASS Summit 2009 I promise to assist anyone with any question.

Chances are I won’t know the answers myself, but I will do my very best to point you in the right direction. And yes, it can be any question, but let’s try to keep things professional.

If any ‘accidental’ DBA’s are actually reading this and are on the fence about attending this year just drop me an email and give me ten minutes of your time to get you as excited about attending as I am.

And to @wnylibrarian I would also like to add the following: Stop thinking of yourself as a ‘technical librarian’ and start thinking of yourself as someone who is technical. Your technical skills will apply outside those stacks of books as well as inside.

See you, and everyone else, at the PASS Summit in Seattle!