SQL Job Notifications

Today on Twitter TJay Belt (blog|twitter) was asking about job notifications, specifically related to being able to add some kind of logic to that notification.

I’m using a trigger on the sysjobhistory in the msdb database to handle this, below is a version with very basic logic, but something easily expanded upon 

 

This process is based upon the script posted by Kenneth Singer back in 2007 on SQL Server Central. You can find that at http://www.sqlservercentral.com/scripts/Miscellaneous/30177/ 

 

USE [msdb]

GO

 

SET ANSI_NULLS ON

GO

SET QUOTED_IDENTIFIER ON

GO

 

CREATE   trigger [dbo].[trg_jobstepfailures]

on [dbo].[sysjobhistory]

for insert

as 

declare @strcmd varchar(800),@strRecipient varchar(500),@strMsg varchar(2000), @strServer varchar(255),@strTo varchar(255)

 

Declare @Subject varchar(500)

 

if exists (select * from inserted where run_status = 0 and step_name NOT IN ( '(job outcome)', 'do not notify'))

begin

    select  @strMsg =  '<FONT FACE="color: #A0522D; font-family: Verdana; font-size: 12; font-weight:bold">' + @@servername + '-Job: ' + sysjobs.name + '. Step= ' + 

     inserted.step_name + '</font><br><br><FONT FACE="color: #A0522D; font-family: Verdana; font-size: 12">Message ' + inserted.message + '</font>'

     from inserted

    join sysjobs

    on inserted.job_id = sysjobs.job_id

    where inserted.run_status = 0

    

--subject text

    select @subject = 'Job ' + sysjobs.name + ' Failed on Job Server' +@@Servername

    from inserted 

    join sysjobs 

    on inserted.job_id = sysjobs.job_id

    where inserted.run_status = 0

 

--recipeints text

    IF @subject like '%<USE NAME OF FAILED JOB HERE>%'

        Begin 

            SET @strRecipient= '<RECPIENT LIST>'

        End

    ELSE

        Begin

            SET @strRecipient= '<ALTERNATE RECIPIENT LIST>'

        End

      

    exec msdb.dbo.sp_send_dbmail @profile_name = null --uses the default profile

     ,  @recipients = @strRecipient

     ,  @subject = @subject

     ,  @body =  @strMsg

     ,  @body_format = 'HTML' --default is TEXT

     

end

 

GO

SQL Server 2000 DTS Logging

On twitter today TJay Belt (blog|twitter) asked if anyone had concerns with enabling logging on DTS packages. This brought to mind some of the logging fun that I have had with SQL 2000 and DTS over the years.

With DTS you have the option of logging to a flat file and also to SQL Server directly.

When choosing the SQL Server option and entering your server name you’ll find that logs are written to msdb.dbo.sysdtspackagelog and individual steps within those packages go to msdb.dbo.sysdtssteplog.

 

 Viewing DTS Package Logs

Looking at the SQL Server stored log is as simple as right clicking on the DTS package itself and select “view log” from the list. I have noticed in the past that this can actually cause significant blocking issues within the msdb database. This process never seemed very intelligent as it appeared to scan all the logs for all packages and then filter out what it didn’t want.

Frequently you are just looking for failures, for this I have a small script which can be run, it returns packages with steps that failed and the failure information for that step. You can limit it to whatever time frame you want, as well as limit it to certain packages.

 

select 

   PL.Name

   , PL.starttime

   , PL.endtime

   , SL.stepname

   , SL.errordescription 

from 

   msdb.dbo.sysdtspackagelog PL(nolock)

      inner join msdb.dbo.sysdtssteplog SL (nolock)

        on PL.lineagefull = SL.lineagefull

where 

    --name = '<job name>' and 

   stepexecresult <> 0

   --and starttime > '2009-06-08 00:00:00' and starttime < '2009-06-09'

order by 

    PL.starttime desc

Keep Your Logs Clean

Something else to keep in mind is that your DTS logs will not clean themselves. It’s worthwhile every week or two to clean up old data. Both sysdtssteplog and sysdtspackagelog have start time and end time columns making any deletes a quick and easy process.

 

ISV Rant

This post was originally inspired by T-SQL Tuesday #5 hosted by Aaron Nelson (blog|twitter) and a great rant by Jeremiah Peschka (blog|twitter) where he discussed the relative merits of encrypted stored procedures and their effect upon his rug http://facility9.com/2010/04/08/encrypted-stored-procedures-and-their-effect-on-my-rug

At work we have an implementation of an enterprise level reporting solution. It’s from a very large international independent software vendor who I shall call IISV (the initial draft named names, however I’ve decided to be a tad more discrete).

The report writers seem to love it. I believe that it gives them the opportunity to quickly develop and deploy reports, which they do on a frequent basis. The trouble is that the code it writes is shall we say, a little poor. In fact this tool is the whole reason that I have had massive blocking issues on a reporting server, although this has luckily provided me with an opportunity to gain a much greater understanding of different transaction isolation levels and the locking methods employed by each when queries are executed. You can read up on this in a previous post “T-SQL Tuesday #005 – Changing Your Isolation Level

So this enterprise level tool, created by this huge company DOES NOT SUPPORT SQL 2008 SP1! Yup, that’s right, a service pack that has been out for over a year is not supported. Incidentally neither is SQL 2005 SP3, however if you are on SQL 2000 SP4 then it’s all good.

Earlier this year the reporting team were advised that we were going to be applying SP1 to all of our SQL 2008 servers. This caused a minor panic as we wouldn’t be supported by said company in the event of a problem. My response was quite simple on this:

“We have a choice, be supported by IISV on SQL 2008 RTM or we can be supported by Microsoft on SQL 2008 SP1. I choose the support of Microsoft because if I have a server issue then your reporting solution isn’t going to be functioning anyway”

 

We’re running SP1 on all our SQL 2008 servers now.

As a side bar, this IISV application has a repository database, one that has been causing deadlocks on our SQL 2000 system since it was implemented. My guess is that it was developed by some Oracle folks because every column on the main table has an index. Additionally, whenever a value is updated in a row rather than just change that value it actually updates every single cloumn, they think that it’s all good though.

While this post has been related to reporting and this one vendor they are far from the only offender with this kind of thing. Another major ISV, for example, recently refused to certify one of their major products with SQL Server 2008, as such said product will be deployed on Oracle.

Is this something that is just an inherent issue with large vendors? They just can’t keep up with the technology and ultimately force you down a road where you have to deploy a solution that is unsupported in some fashion?

I makes wonder if there is something to be said for going with some of the smaller shops and tools given that they are more likely to be agile in keeeping up with version support.

T-SQL Tuesday #005 – Changing Your Isolation Level

 The current T-SQL Tuesday is being hosted by Aaron Nelson (blog|twitter) who has come up with a great topic, that of “Reporting”.

Reporting is huge in scope, and Aaron was very helpful in throwing out a couple of ideas to get started. I went back and forth on what to write about, ultimately I thought I’d put up something which has made a huge difference to me recently as regards blocking issues in my reporting environment.

 

The Problem

Monitoring is one of the mainstays of the DBA life. Thomas LaRock (blog|twitter) dedicated a good section of his must read book “DBA Survivor: How To Be A Rock Star DBA” to it. Given that I am trying to be a good DBA I have various monitoring routines in place, one of these being to check for blocking on my servers.

The main production reporting server (running SQL 2008 EE) was beating me up with emails to the tune of a couple of hundred a day. Sadly we have a tool that allows folks to drag and drop to add items to queries and execute them. This does not create great query plans, we frequently have long running queries and these block the replication procs.

Suffice to say this couldn’t continue, but what to do?

 

The Solution

Not being able to change any of the code for the reports or do anything with the software I was really at an impass, until over the horizon strode changing the locking method on the database as a solution.

 

Transaction Isolation Levels

SQL Server has 5 different ways that you can hold (or not) a lock when attempting to read data.

  • Read Uncommitted – holds a schema shared lock
  • Read Committed – holds a shared lock that is immediately released
  • Repeatable Read – holds a shared lock for the life of the transaction
  • Serializable – holds a shared range lock for the life of the transaction
  • Snapshot  – holds a schema shared lock and uses row versioning

Within a query or procedure you can specify one of these to use, each will impact how data is accessed.

By default SQL uses the Read Committed isolation level. This holds a lock while the transaction is running to ensure consistency of the data, and is the reason why I was having a blocking nightmare.

Frequently in reporting situations (and I have to admit I am guilty of this) people use the NOLOCK hint, this is the same as the Read Uncommitted isolation level. While it eliminates the locking problem you also run into a situation where you can quite easily get dirty reads and produce the wrong data in a report (because you are reading uncommitted data you could read something someone else is updating within a transaction, they could roll that change back however you would have still read what was in there at the time). I really didn’t want to have dirty reads, and in any case I could not touch the code that was coming in, so that would not have helped. I needed something else.

 

Row Versioning

Row versioning is something not oft used or very well known about for SQL Server, but it can provide huge benefits in situations like this. By changing a couple of database options we can use row versioning to ensure that we get consistent reads and no blocking problems (except in circumstances where we would be looking to change the schema, there are always caveats).

Here’s how it works. When you open a transaction and perform an update an entry is placed into a version store in TempDB. This version store contains the original row. Should anyone come along and attempt to read that data they are redirected to the original row in the version store and so can continue on their merry way without being blocked. Once the transaction is committed the row is removed from the version store and you would be back to reading the actual table again. This is all invisible to the user (in fact this is so invisible that you won’t see reads against the version store or TempDB when turning on statistics or looking at an execution plan).

To enable row versioning on the AdventureWorks2008 database you would run the following script;

ALTER DATABASE AventureWorks2008
SET ALLOW_SNAPSHOT_ISOLATION ON
GO
ALTER DATABASE AventureWorks2008
SET READ_COMMITTED_SNAPSHOT ON
GO

 

In order to enable these options you can’t have any other open transactions in the database, so either kick everyone out or add WITH ROLLBACK IMMEDIATE to the scripts which will kill any open transactions and send them into rollback.

 

Caveats!

See, like I said, there are always caveats.

While this is all good and well you’ll want to keep a close eye on TempDB. The version store lives in TempDB and depending upon the number of transactions flowing in your server and the ongoing activity you might blow it out of the water. Make sure that it’s properly sized and well monitored, use the “Transactions: Free Space In TempDB” perfmon counter to keep track of it. In you perform bulk loads this might not be a good road for you to go down because of this.

Watch your disk performance. A lot more work on TempDB means that those disks are going to get hit more heavily.

Keep an eye on the version store and how big it gets, sys.dm_tran_version_store holds all of those records and can be queried like any other table.

Last, but certainly not least BE WARNED! You will be adding a 14 byte overhead to every row so that it can hold version store information. This might sneak up on you as these 14 bytes are not added until such time as you update a row, so one day you could perform a mass update and actually wind up using a whole lot more disk. The SQLCAT team have posted a really fantastic article at http://blogs.msdn.com/mssqlisv/archive/2009/05/18/why-did-the-size-of-my-indexes-expand-when-i-rebuilt-my-indexes.aspx regarding row versioning and the impact on table size as a result of online and offline indexing. It’s a must read if you are thinking of a trial implementation. 

 

Buck Woody (blog|twitter) has a great disclaimer about never trusting scripts, trying them yourselves in a test environment. I heartily agree with him. Please don’t just throw this on your production server and hope for the best.

Plagiarism

There is some discussion uproar as regards a case of ongoing plagiarism within the SQL community. You can find out all the details at http://www.brentozar.com/archive/2010/03/plagiarism-inspiration-and-john-dunleavy and http://itknowledgeexchange.techtarget.com/sql-server/plagiarism-inspiration-and-john-dunleavy/?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+techtarget%2FwIpw+%28SQL+Server+with+Mr.+Denny+-+SQL%29

 

This is not something new. Brent Ozar (blog | twitter) has brought this up on previously with the invidual in question. John Dunleavy (blog | twitter) has been caught time and again using other peoples content, and has in fact been caught posting carte blanche Microsoft White Paper articles including the copyright notices.

Brent Ozar and Denny Cherry (blog | twitter) have been absolutely right in calling out John for his plagiarism.  Sure it’s very easy to copy someone elses work (even just pieces) that does not make it right however. That’s part of why I don’t post much, or often, I’m busy trying to come up with something of my own. While a concept of what I am saying might have been posted out elsewhere I’m actively looking to put my own spin on it and make it my own. Heck, most of what I read is way above my level, so there’s nothing good I can add, as such I just leave it alone.

I’m not looking to aggregate other peoples content, that doesn’t make much sense to me. There are plenty of aggregators out there, and that’s hardly going to be my voice. That’s what all these blogs should be about, someones voice. What comes out of my blog is mine, I stand behind what I say, I even hope that some of it is useful to people.

People like Brent, Denny, Jonathan Kehayias (blog | twitter), Jorge Segarra (blog | twitter), Paul Randal (blog | twitter) and many many others dedicate a great deal of their time and energy in giving to the community. They blog, they tweet, they answer forum questions, they don’t get paid for it (Denny might break even as he has stated on his blog, however he’s hardly attempting to make a living off it). Why do they do it? I don’t know, you’ll have to ask them, however I believe that these people just have a passion for what they do. They enjoy it, they have a huge enthusiasm for it and they just want to share that with the world at large. 

One of the most amazing things about SQL Server is the community. If you have a question or problem there are so many places that you can turn to for answers. A post on twitter with the #SQLHELP tag, for example, will generally get you an answer inside of 30 minutes, and usually includes a link to a blog post or article somewhere on the web where you’ll find in depth information and it doesn’t cost you a dime. There are all the free training resources available that I’ve linked in a my “Ehance Your Skills For Free” post, all based around the community.

When someone like John Dunleavy plagiarizes others, aside from it being just wrong, it hurts the community. It makes people less likely to post stuff; makes folks less likely to help others; it makes people like me write poorly constructed blog posts because they are miffed.

 

I think the final note on this comes from the movie Downfall and a translation provided by Chuck Boyce Jr (twitter) which can be viewed at http://www.youtube.com/watch?v=4DWR3zdUsQA

 

 

TSQL Tuesday #004 – Who’s Using My IO?

This blog post is in the response to the T-SQL Tuesday #004: IO post by Mike Walsh.

I ran into a problem recently whereby I was seeing some disk queuing and it was impacting performance. Nothing much new there, these things happen. However I was fortunate in that some new disk had just been added to the server and there was an opportunity to offload some of the i/o on to some new spindles. 

 Rather than just throw a couple of databases or logs onto the new disk I decided to take a look and see where the majority of my i/o was coming from. I thought that I knew where it was and knew what I was going to move. I was surprised by what I actually found out.

There’s a new function in SQL 2005/2008 called sys.dm_io_virtual_file_stats. You pass in the database id and file id to get a list of statistics including i/o bytes read and written, number of i/o reads and writes and any i/o stalls or waits. What’s great is that if you pass in null for the two function paramters you get back information for every file on the SQL Instance.

SELECT * FROM sys.dm_io_virtual_file_stats(null, null)

 

 

 

This information would be very useful if you could make head or tail of half the information. This is where the system view sys.master_files comes in very useful. This view contains information about every file on the SQL Instance; what it is, where it is, it’s state, size, growth, max size and more.

SELECT * FROM sys.master_files

 

 

If we join the two together we can easily see what files are showing the heaviest i/o on reads, writes or combined. 

SELECT DB_NAME(svs.database_id) AS DatabaseName

, smf.name AS InternalName

, smf.physical_name AS PhysicalFile

, smf.type_desc AS FileType

, CONVERT(NUMERIC(15,2), CONVERT(NUMERIC(15,2),io_stall_read_ms) / num_of_reads) AS AverageReadWait_ms

, CONVERT(NUMERIC(15,2), CONVERT(NUMERIC(15,2),io_stall_write_ms) / num_of_writes) AS AverageWriteWait_ms

, CONVERT(NUMERIC(15,2), ((CONVERT(NUMERIC(15,2),io_stall_read_ms) / num_of_reads) 

       +  (CONVERT(NUMERIC(15,2),io_stall_write_ms) / num_of_writes)) / 2) AS AverageWait_ms

  FROM sys.dm_io_virtual_file_stats(NULL, NULL) svs

INNER JOIN sys.master_files smf

ON svs.database_id = smf.database_id 

AND svs.file_id = smf.file_id

ORDER BY AverageWait_ms DESC

 

 

Using this I was able to quickly identify the worst performing database, what file within that database was being hardest hit and move it out to it’s own set of spindles and greatly improve read/write times across the board.

One thing to note, this is based upon the understanding that the indexes are correctly set and utilized on the database in question and that files and filegroups allocations are adjusted already for the best performance. This is far from a catchall and simply adding a covering index to an existing table could provide significant improvements. This is just something to help along the road.

 

Enhance Your Skills For Free

Times Are Tough. Companies are struggling to survive and layoffs common. There is very little budget (company or personal) to get yourself the training that you need to keep your skills current or allow you to move up to the next level.
Managers frequently find themselves with no budget for staff training, so even when you are able to provide a strong business case you aren’t going to get the opportunity (short of paying for it yourself).

  

Get Some Free Training

The SQL Server community is quite amazing in the resources that it provides to help it’s members new and old with scripts, advice and also training. All that needs to happen is for you to actually utilize these resources and learn something new.

 

SQL Saturday

Free one day training events in locations all around the US. Run by volunteers and supported by vendors, SQL Saturday was started by Andy Warren, Brian Knight and Steve Jones it has recently been acquired by PASS. I like to equate it to a mini-PASS Summit as you’ll find a bunch of presentations for different skill sets with different areas of focus.

 SQL Lunch

Take 30 minutes and learn something. With the tagline “No Fluff, Just Stuff” Patrick LeBlanc started SQL Lunch, it’s aim being to provide you with intense compressed knowledge without the filling.  Along with providing a fresh new live webinar every couple of weeks you’ll find all of the old presentations hosted at the website where you can get to them any time.

SQL Community Webinars

Hosted up on the Pragmatic Works site a group of authors from the SQL community have provided their time and knowledge and are hosting live webinars. Supported by WROX, SQL Server Central and Pragmatic Works February was all based around SQL Server 101 with March focusing on BI. All of the webinars are recorded so you can go back and watch them whenever you are ready.

Microsoft Webcast, Videos & Labs

You can head out to Microsoft to find a great deal of learning materials. You’ll find webcasts both live and recorded, technet videos, podcasts and most importantly virtual labs. It can be a challenge to set up some of the complex environments that are needed for learning certain things such as peer to peer replication, having these labs eliminates that as a problem. It provides you a great opportunity for hands on learning. You can check out sections for SQL 2008, 2005 and BI.

 

These are just a few of the resources out there. The resources are great, but it is up to you to take advantage of them.

 

A SQL Saturday will take you away from your family for a day, but it provides you with a lot, the other items listed you could take at work. Speak to your manager, tell them that you can get some training and it won’t cost them anything other than an hour or two of your time a week, a pretty easy sell.

Odd SQL 2005/2008 Query Behaviour

This is just strange…
If a column appears twice in your query and you attempt to use it within an order by statement you get an error, unless you fully qualify the column. This doesn’t happen in 2000, but does in 2005 & 2008
This doesn’t work:

select name, * from sysobjects
order by name

Msg 209, Level 16, State 1, Line 3
Ambiguous column name ‘name’.

 

This does:

select name, * from sysobjects s
order by s.name

SQL Monitoring On The Cheap

Monitoring tools are costly, a pain to get running (especially within an enterprise) and they frequently don’t provide me with what I want or how I want it. I’m not always looking for real-time troubleshooting and critical notification, sometimes I just want to be able to perform some simple tasks to keep an eye on my SQL Server Instances. Little things like regular checks on physical nodes, disk space, database space, etc… I want to be able to manage this simply and get a regular report that will show me anything out of the ordinary and keep some historical data so that I can go back and look for trends and such.

Born from this wish was, as my good friend and colleague Shawn coined, our SQL Server Dashboard *fanfare*

Is ourSSD (cool name huh) really a dashboard? Showing you would spoil the surprise and take away all of my fun so sorry, no screencaps just yet. I can tell you that ourSSD is fairly straightforward, easily adaptable, scalable and modular. In fact you can throw something new into it or rip something out of it any time that you want. You can set your own thresholds for this that and the other thing…blah blah blah…oh, and it didn’t cost anything other than time. ourSSD provides me (and Shawn bless him) great little snapshots of the information that I am interested on a daily basis.

Just as a warning though, ourSSD has no pretty little gauges or flashing lights, there isn’t a super exotic code base or anything. Heck! You are not even going to find a nicely formatted SSRS report (although there easily could be). What you will find are a couple of packages, some scripts and a plain boring html email sent out a few times a day that contains everything you want to know and are afraid to ask.

I’ll be putting a series of posts out with the things I’ve got hooked into ourSSD already, how they work, what they use and why I use them. Suggestions for other items would be greatly appreciated, and if you can tell me how to do them, even better .

Why?

Such a simple question really. I wish I knew the answer. Actually, I wish I knew the answers to a great many things, which is how I got here.

I’ve been a SQL DBA for almost 10 years now (where did that time go?) and as each year passes I find that I know less and less, or rather that I am exposed to more and more and so discover that I know less and less. Ignorance sure is bliss. There are a lot of blogs and sites etc out there in the ether, I’ll link to some and then pose some questions, try to answer my own questions and just put general items out there.

This will either be very helpful (to me, even if nobody else) or a complete disaster, time will tell.