Av rating:
Total votes: 17
Total comments: 20


Randy Volters
DML Trigger Status Alerts
04 July 2008

When databases suddenly stop working, it can be for a number of different reasons. Human error plays a large part, of course, and the DBA needs to know what these various humans are up to. DDL triggers can help alert the DBA to unauthorized tampering with a production system, of course, but DDL triggers can't tell you everything. At some point, you will need to implement your own checks.

A method for tracking DML trigger status on SQL Server 2005

A need for this method - Background

Several weeks ago, during a routine day, our call center started reporting difficulties with sales orders.  Later that same day, our warehouse started reporting problems with our shipping software. They were reporting  missing records when they were scanning packages to ship. Our ERP system has been modified to use DML triggers to push sales orders through the print picking ticket/post sales order/post shipment process as well as interfacing to our shipment software to hand off a shipping record and hand back a tracking number.  The status of several orders indicated that they were getting ‘stuck’ in various stages of this auto-post process, instead of completing as they should. Since we’re still in the process of working out the kinks in this new system and since we’ve had these types of issues before, due to bugs in the modified code, I issued a support call to our vendor. When the vendor answered the call and we started troubleshooting this issue, we found that we were not seeing what we had expected based on past experience.  At one point during the conference call/Webex session, the developer stated, “it is almost as if some triggers are disabled”.  When we began checking the status of triggers, we found that someone had disabled all the triggers on the sales order header table! This precipitated two actions. 

  1. taking appropriate steps to review with senior management the need to put (back) into place a process whereby only internal system administrators will be members of db_owner and external consultants will be removed from this db_owner group and will have to call in to have their logins enabled while they troubleshoot our systems during a monitored Webex session;
  2. a project to ascertain how we might be notified pro-actively in the event a trigger(s) is disabled on our system(s).

A DDL Trigger approach?

At first, it seemed the most obvious technology to use for the project goal was a DDL Trigger.  I had recently re-read Robyn Page’s article on DDL Triggers and their usage, and was keen to try this approach.  However, a problem with the EVENTDATA function surfaced immediately.  Based on examination of BOL it appeared that If the ALTER TABLE command were used to disable / enable a DML trigger the intended information could be captured.  But if the DISABLE TRIGGER, ENABLE TRIGGER, or the GUI of the Management Studio, were used to change a trigger’s status, the information would not be collected.

A quick test using the Studio’s GUI and a prototype DDL trigger proved this assumption correct.  After a few days of prowling Red Gate and Microsoft forums I gathered from various posts that in point of fact, there is no way to capture information about an ENABLE TRIGGER or a DISABLE TRIGGER command in EVENTDATA.  (Since this was the case, Event Notification was also out of the question).

(This assumption about EVENTDATA may be incorrect, but the need was pressing, the facts available supported this conclusion, and so I decided to move on.)

DBMail – the solution (for now)

Since EVENTDATA appears to be useless to the task at hand, the two mechanisms that seemed to meet the pro-active notification requirement – DDL Trigger and Event Notification - were out of the running. We’ve been using the DBMail functionality here for some time.  We’ve found it to be 100% reliable to date.

The concept of a job scheduled to ‘spin’ every minute was settled on.  If the overhead were low, it should not burden the system.  If a trigger were disabled and re-enabled before the job could spin and detect this, then “no harm, no foul”.  Well actually there is a small window of risk, but this was deemed acceptable for our environment.

So, the current (for the present) solution to the problem of getting  g notified about a DML trigger status change is to schedule a TSQL job to run every minute, 24x7, and if the status of a trigger has changed in the last minute, log to a current status table and send an email.

USE [DBA]                                   -- my database for all audit tables on the system

GO

/****** Object:  Table [dbo].[tbl_triggerAuditRpt] */

SET ANSI_NULLS ON

GO

SET QUOTED_IDENTIFIER ON

GO

SET ANSI_PADDING ON

GO

CREATE TABLE [dbo].[tbl_triggerAuditRpt](

      [auditDate] [datetime] NOT NULL,

      [triggerName] [sysname] COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,

      [is_disabled] [bit] NOT NULL,

      [triggerStatus] [varchar](8) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,

      [trigger_table] [sysname] COLLATE SQL_Latin1_General_CP1_CI_AS NULL,

      [who_changed] [nvarchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,

      [userName] [nvarchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,

      [suserName] [nvarchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,

      [originalLogin] [nvarchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NULL

) ON [PRIMARY]

 

GO

SET ANSI_PADDING OFF

USE dba                                               -- my database for all audit tables on the system

;

 

IF EXISTS(SELECT * FROM sysobjects WHERE NAME = 'tbl_triggerAuditFires' AND TYPE = 'u')

DROP TABLE tbl_triggerAuditFires  -- checks for status change in triggers

;

 

USE <snip>                                          -- name of ERP database, put your database to monitor here

;

            SELECT GETDATE() AS auditDate, sys.triggers.NAME AS triggerName, sys.triggers.is_disabled,

            CASE WHEN sys.triggers.is_disabled = '1' THEN 'Disabled' ELSE 'Enabled'

            END AS triggerStatus,

            sysobjects.NAME AS trigger_table, dt.who_changed,

            (SELECT (CONVERT([nvarchar](50),user_name(),(0))) ) AS [userName],

            (SELECT (CONVERT([nvarchar](50),suser_sname(),(0))) ) AS [suserName],

            (SELECT (CONVERT([nvarchar](50),original_login(),(0)))) AS [originalLogin]

                        INTO dba.dbo.tbl_triggerAuditFires

                        FROM

-- if/when user process making change is using ERP login ‘psqladm’ get the real username

                        (SELECT CASE WHEN SESSION_USER = 'psqladm' THEN

                                    (SELECT cast(SUBSTRING( b.HOST_NAME,2,(CHARINDEX('>',b.HOST_NAME)-2) )

                                     AS nvarchar(50)) AS who_changed

                                     FROM sys.sql_logins a

                                     INNER JOIN sys.dm_exec_sessions b

                                     ON a.sid = b.security_id

                                     WHERE a.NAME = 'psqladm'

                                     AND b.session_id = (select @@SPID))

                                    WHEN SESSION_USER <> 'psqladm' THEN

                                    (SELECT CAST(SESSION_USER as NVARCHAR(50)))

                                    END as who_changed) as dt,

            sys.triggers

            LEFT OUTER JOIN sysobjects

            ON sys.triggers.parent_id = sysobjects.id

            ;

 

USE dba

;

 

IF EXISTS (SELECT * FROM sysobjects WHERE NAME = 'tbl_triggerChangeDecision' AND TYPE = 'u')

DROP TABLE tbl_triggerChangeDecision  -- for mailing status changed rows

;

 

SELECT a.*

INTO dba.dbo.tbl_triggerChangeDecision

FROM dba.dbo.tbl_triggerAuditFires a                       -- checks for status change

INNER JOIN dba.dbo.tbl_triggerAuditRpt b                -- holds current status of triggers

ON a.triggerName = b.triggerName

WHERE a.triggerStatus <> b.triggerStatus

;

 

-- if there has been a status change, then send alert mail to sysadmins

IF (SELECT COUNT(*) FROM dba.dbo.tbl_triggerChangeDecision) >

BEGIN

;

 

             DECLARE @TRGRRCHNG  NVARCHAR(MAX) ;

             SET @TRGRRCHNG   =

                N'<font face="Calibri"><H1>Trigger Change Alert</H1>' +

                N'<H2>The following trigger(s) had their status changed</H2>' +

                N'<table border="1">' +

                N'<tr><th>auditDate</th>' +

                        N'<th>Trigger Name</th>' +

                N'<th>NEW Trigger Status</th>' +

                N'<th>Trigger Parent Table</th>' +

                N'<th>Who Changed</th>' +

                N'<th>Original Login</th>' +

                       

                        CAST( (

                                                select td = auditDate ,  '',

                                                td = triggerName ,  '',

                                                td = triggerStatus ,  '',

                                                td = trigger_Table ,  '',

                                                td = who_changed ,  '',

                                                td = originalLogin

                                                from dba.dbo.tbl_triggerChangeDecision

                        FOR XML PATH('tr'), TYPE

                ) AS NVARCHAR(MAX) ) +

                N'</font></table>'

            ;

           

           

            EXEC msdb.dbo.sp_send_dbmail   

                @profile_name = 'sqlmail',

-- your email addresses here

                        @recipients = '<sysadmin@email.com>; <sysadmin2@email.com>',

                        @subject = 'FROM SERVER03 TRIGGER ALERT !!! TRIGGER WAS CHANGED',

                @body = @TRGRRCHNG    ,

                @body_format = 'HTML'

            ;

 

TRUNCATE TABLE dba.dbo.tbl_triggerAuditRpt          -- empty out current status of triggers tbl

;

 

INSERT INTO dba.dbo.tbl_triggerAuditRpt                 -- add updated status of triggers as new current status

SELECT * FROM dba.dbo.tbl_triggerAuditFires

;

 

END

;

When the job fires and captures a change, this is what the system administrators receive in their inbox:

and when corrected …

Criticisms and Responses

I do not claim that this approach is foolproof, but it is much better than what we had prior to this project. However, I have some criticisms to myself and responses to myself about the project in its current state:

Criticism: Someone with malignant intentions can turn the job off!

Response: Yes, but to do so, they need to examine a long list of jobs and ascertain that a job actually exists to monitor this.  Actions I’m contemplating once I’m satisfied with this approach include placing the TSQL into an encrypted stored procedure (after backing up the code to an offline storage system our sysadmins maintain); this makes the task even more difficult as I will then have a dubiously named job that calls a stored procedure that cannot be listed out.  (Note to self, “is there a way to monitor and detect if a job has been disabled?”)

Criticism: What if Exchange goes down and the alert mail is not delivered?

Response: The table name ‘tbl_triggerAuditRpt’ suggests a report might exist.  It does.  SQL Server Reports is being extensively used and a new “DBA Report” will be added that shows this table (i.e., a report of the current trigger status.  Yes, it is not “Pro-Active”, but it does tend to get watched (as all the DBA Reports do) more closely than most other status reports.

Criticism: You are not capturing an audit trail of trigger status change over time!

Response: There is really no need.  The information of import is that a trigger is disabled.  All that is needed is to: 1) know it is off so it can be turned back on; 2) identify who did this so corrective action can be taken.



This article has been viewed 4113 times.
Randy Volters

Author profile: Randy Volters

Randy Volters began his career in the data processing industry in 1978. His first exposure to programming in SQL was in the early 80’s on a Burroughs Corporation mainframe computer. He has held numerous roles in Sales, Marketing, Product Management and Software engineering with various companies, including Burroughs Corporation and NCR Corporation. In 2001 he was certified a Teradata Master and has consulted on several data warehouse projects. Currently he is working for a leading Wholesale and Service company in the foodservice industry where he holds the title of Chief Data Architect and is overseeing the OLTP, ERP, and Data Warehousing efforts there, all of which are hosted on Microsoft SQL Server. Randy lives in Columbia, South Carolina.

Search for other articles by Randy Volters

Rate this article:   Avg rating: from a total of 17 votes.


Poor

OK

Good

Great

Must read
 
Have Your Say
Do you have an opinion on this article? Then add your comment below:
You must be logged in to post to this forum

Click here to log in.


Subject: Your script errors.
Posted by: Anonymous (not signed in)
Posted on: Friday, July 04, 2008 at 12:03 PM
Message: You are referencing tables before they exist in scope because you have not created them yet.

Msg 208, Level 16, State 1, Line 85
Invalid object name 'dba.dbo.tbl_triggerAuditRpt'.

Subject: tbl_triggerAuditRpt
Posted by: randyvol (view profile)
Posted on: Monday, July 07, 2008 at 8:18 AM
Message: As implied in the article, this table IS outside the scope of the trigger, having been created for the purpose of holding data from the trigger audit. I never intended for the trigger to create the table, rather, as the truncate command illustrates, when the trigger fires the table is emptied and new information is inserted.

I've now added the create statement for tbl_triggerAuditRpt to the source code of the article so that nobody else hits the same problem.

Subject: "is there a way to monitor and detect if a job has been disabled?”
Posted by: Anonymous (not signed in)
Posted on: Monday, July 07, 2008 at 3:44 PM
Message: Randy,

the answer is a definitive yes. I am writing a tool set to do just that, and also to do your watching for trigger status changes, and more. For the time being, if you want to get notified for the job's status, check out the table msdb.dbo.sysjobs. There is a column named enabled. Write a similar job like yours to send an alert.

To be sure, you also need to check on the job schedule -- if the schedule gets disabled, the job would not run either.

Btw, I checked with MS GUI. Enable/Disable trigger with it does get captured with the DML trigger.

Enjoyed reading your article.

Quentin (qran2 @yaoo.com, take out the space before @)

Subject: "is there a way to monitor and detect if a job has been disabled?”
Posted by: Anonymous (not signed in)
Posted on: Monday, July 07, 2008 at 4:00 PM
Message: Randy,

the answer is a definitive yes. I am writing a tool set to do just that, and also to do your watching for trigger status changes, and more. For the time being, if you want to get notified for the job's status, check out the table msdb.dbo.sysjobs. There is a column named enabled. Write a similar job like yours to send an alert.

To be sure, you also need to check on the job schedule -- if the schedule gets disabled, the job would not run either.

Btw, I checked with MS GUI. Enable/Disable trigger with it does get captured with the DML trigger.

Enjoyed reading your article.

Quentin (qran2 @yaoo.com, take out the space before @)

Subject: Thanks Quentin ...
Posted by: randyvol (view profile)
Posted on: Tuesday, July 08, 2008 at 8:02 AM
Message: Of course! Why didn't I think of that? Oh well, must be getting old. ;-) Ok, so now we have a job to monitor the DML trigger changes AND a means to monitor if the job gets disabled.

Tony suggested an alternative which would be to use osql in a scheduled task to fire off the job; which puts it outside SQL Server jobs and makes it more difficult to find, but then you'd not be able to monitor sysjobs to see if it was disabled.

But it does give one a choice of how to approach this problem space.

Regards.

Randy

Subject: correction to my first post in this thread...
Posted by: randyvol (view profile)
Posted on: Tuesday, July 08, 2008 at 8:05 AM
Message: When reading my reply post about the table, tbl_triggerAuditRpt please mentally substitute the word job where I typed the word, 'trigger'.

I don't know where my head was at. There is no trigger here. It is a job that monitors the status of existing triggers.

Must not have had enough coffee when I wrote this, sorry for the confusion.
;-)

Randy

Subject: Thanks
Posted by: dibmaz (view profile)
Posted on: Tuesday, July 15, 2008 at 4:14 AM
Message: Nice article

Subject: "is there a way to monitor and detect if a job has been disabled?”
Posted by: Reuben (not signed in)
Posted on: Tuesday, July 15, 2008 at 5:02 AM
Message: I think ideally you would have a separate, DBA-only "control" server, for monitoring the SQL instances in your environment. Nobody internal or external, except your DBA's, should require access and as such it can securely and safely carry out any checks you require.

Secondly, I would personally want a record a history of when and for how long the trigger status has been disabled. Won't you occassionally need to disable it on purpose for upgrades?

Subject: "is there a way to monitor and detect if a job has been disabled?”
Posted by: Reuben (not signed in)
Posted on: Tuesday, July 15, 2008 at 5:55 AM
Message: I think ideally you would have a separate, DBA-only "control" server, for monitoring the SQL instances in your environment. Nobody internal or external, except your DBA's, should require access and as such it can securely and safely carry out any checks you require.

Secondly, I would personally want a record a history of when and for how long the trigger status has been disabled. Won't you occassionally need to disable it on purpose for upgrades?

Subject: Tool Set
Posted by: Mike Z (not signed in)
Posted on: Tuesday, July 15, 2008 at 8:13 AM
Message: Great article Randy, thanks!
I'm curious if you've considered incorporating your trigger audit into a toolset like Quentin.

In other words, checking the job schedule and using table msdb.dbo.sysjobs to monitor the jobs' status.

Please continue!
Mike

Subject: Try Catch
Posted by: Paul (view profile)
Posted on: Wednesday, July 16, 2008 at 2:34 AM
Message: Loved the article. Since this is related to changes, maybe you can tell me how you handle the fact that SQL 2005 Try Catch doesn't handle schema changes... The SQL 2005 Try Catch is a very welcome addition (at least in concept - since it simplifies coding errorhandling tremendously) to tsql, but we've noticed that the Catch does not catch everything. In short I was wondering how other dbas/tsql developers are handling of the unhandled errors. This is especially true where code resides in a SP and the underlying schema changes. The code that is already compiled (SP) does not know that about the pblm until it is executed. Furthermore, the Catch in the SP does not catch the error.

-- It is really easy to reproduce
-- using these 7 steps
-- This is a quick test to reproduce that
-- Catch of Try Catch on tsql
-- does not catch (handle) everything.
-- This is serious, coz in our mission critical
-- systems we simply cannot rely on it

-- Step 1 - Create a table on a some_dummy_db and add 2 records:
use some_dummy_db
go

create table testtable (Col1 int, Col2 varchar(20))
insert into testtable values (1, 'val1')
insert into testtable values (2, 'val2')

-- Step 2 - compile a sp that will result in
-- a handled error when executed
create proc usp_illistrate
as
BEGIN
begin try
begin tran -- starts a transaction
-- this shows something that will work
update testtable set col2 = 'val3' where col1 = 1
-- now force an error that will be caught
select 1/0
-- this shows something that will not be caught
update testtable set col2 = 'this error is not caught'
commit -- this will not be reached
end try
begin catch
print 'in (catch) error handler'
IF @@TRANCOUNT > 0
ROLLBACK
DECLARE @ERRMSG NVARCHAR(4000), @ERRSEVERITY INT
SELECT @ERRMSG = ERROR_MESSAGE(),
@ERRSEVERITY = ERROR_SEVERITY()
RAISERROR(@ERRMSG, @ERRSEVERITY, 1)

end catch
END

-- STEP 3 - Execute the SP that will result in a handled error
-- this returns this error because of the forced error:
-- Divide by zero error encountered.
-- notice you know for a fact you're in the error handler
-- because the first msg is: "in (catch) error handler"
-- and then "Divide by zero error encountered."
EXEC usp_illistrate

-- STEP 4 - Now recompile the SP,
-- to show that compiled code may not catch
-- errors as a result of underlying schema changes.
-- Notice that I have just removed the forced error
alter proc usp_illistrate
as
BEGIN
begin try
begin tran -- starts a transaction
-- this shows something that will work
update testtable set col2 = 'val3' where col1 = 1
-- this shows something that will not be caught
update testtable set col2 = 'this error is not caught'
commit -- this will not be reached
end try
begin catch
print 'in (catch) error handler'
IF @@TRANCOUNT > 0
ROLLBACK
DECLARE @ERRMSG NVARCHAR(4000), @ERRSEVERITY INT
SELECT @ERRMSG = ERROR_MESSAGE(),
@ERRSEVERITY = ERROR_SEVERITY()
RAISERROR(@ERRMSG, @ERRSEVERITY, 1)

end catch
END

-- Step 5 - this simulates someone changing
-- the underlying table schema
-- by renaming a field
EXECUTE sp_rename N'dbo.testtable.Col2', N'Col3', 'COLUMN'

-- Step 6
-- this just retrieves the records so that you
-- can see that col2 is renamed to col3
select * from testtable

-- Step 7
-- now see how the catch does not catch the error
-- as you don't receive the 1st msg from
-- the error handler, namely "in (catch) error handler"
EXEC usp_illistrate



Subject: Try Catch
Posted by: Paul (view profile)
Posted on: Wednesday, July 16, 2008 at 2:38 AM
Message: Loved the article. Since this is related to changes, maybe you can tell me how you handle the fact that SQL 2005 Try Catch doesn't handle schema changes... The SQL 2005 Try Catch is a very welcome addition (at least in concept - since it simplifies coding errorhandling tremendously) to tsql, but we've noticed that the Catch does not catch everything. In short I was wondering how other dbas/tsql developers are handling of the unhandled errors. This is especially true where code resides in a SP and the underlying schema changes. The code that is already compiled (SP) does not know that about the pblm until it is executed. Furthermore, the Catch in the SP does not catch the error.

-- It is really easy to reproduce
-- using these 7 steps
-- This is a quick test to reproduce that
-- Catch of Try Catch on tsql
-- does not catch (handle) everything.
-- This is serious, coz in our mission critical
-- systems we simply cannot rely on it

-- Step 1 - Create a table on a some_dummy_db and add 2 records:
use some_dummy_db
go

create table testtable (Col1 int, Col2 varchar(20))
insert into testtable values (1, 'val1')
insert into testtable values (2, 'val2')

-- Step 2 - compile a sp that will result in
-- a handled error when executed
create proc usp_illistrate
as
BEGIN
begin try
begin tran -- starts a transaction
-- this shows something that will work
update testtable set col2 = 'val3' where col1 = 1
-- now force an error that will be caught
select 1/0
-- this shows something that will not be caught
update testtable set col2 = 'this error is not caught'
commit -- this will not be reached
end try
begin catch
print 'in (catch) error handler'
IF @@TRANCOUNT > 0
ROLLBACK
DECLARE @ERRMSG NVARCHAR(4000), @ERRSEVERITY INT
SELECT @ERRMSG = ERROR_MESSAGE(),
@ERRSEVERITY = ERROR_SEVERITY()
RAISERROR(@ERRMSG, @ERRSEVERITY, 1)

end catch
END

-- STEP 3 - Execute the SP that will result in a handled error
-- this returns this error because of the forced error:
-- Divide by zero error encountered.
-- notice you know for a fact you're in the error handler
-- because the first msg is: "in (catch) error handler"
-- and then "Divide by zero error encountered."
EXEC usp_illistrate

-- STEP 4 - Now recompile the SP,
-- to show that compiled code may not catch
-- errors as a result of underlying schema changes.
-- Notice that I have just removed the forced error
alter proc usp_illistrate
as
BEGIN
begin try
begin tran -- starts a transaction
-- this shows something that will work
update testtable set col2 = 'val3' where col1 = 1
-- this shows something that will not be caught
update testtable set col2 = 'this error is not caught'
commit -- this will not be reached
end try
begin catch
print 'in (catch) error handler'
IF @@TRANCOUNT > 0
ROLLBACK
DECLARE @ERRMSG NVARCHAR(4000), @ERRSEVERITY INT
SELECT @ERRMSG = ERROR_MESSAGE(),
@ERRSEVERITY = ERROR_SEVERITY()
RAISERROR(@ERRMSG, @ERRSEVERITY, 1)

end catch
END

-- Step 5 - this simulates someone changing
-- the underlying table schema
-- by renaming a field
EXECUTE sp_rename N'dbo.testtable.Col2', N'Col3', 'COLUMN'

-- Step 6
-- this just retrieves the records so that you
-- can see that col2 is renamed to col3
select * from testtable

-- Step 7
-- now see how the catch does not catch the error
-- as you don't receive the 1st msg from
-- the error handler, namely "in (catch) error handler"
EXEC usp_illistrate



Subject: DBA control server ?
Posted by: randyvol (view profile)
Posted on: Thursday, July 17, 2008 at 11:43 AM
Message: Hi Reuben -
You suggested "I think ideally you would have a separate, DBA-only "control" server"...

I wish I lived in an ideal world ;-)

But then if I did I wouldn't have needed to write this article ;-)

Reality is we have a limited budget and I cannot get my boss to see the value in a "DBA only" server. Wish I could. The best I can do is to have my own database on the production server; and limit access to it.

Subject: Toolset?
Posted by: randyvol (view profile)
Posted on: Thursday, July 17, 2008 at 11:47 AM
Message: Hi Mike -

You asked, "I'm curious if you've considered incorporating your trigger audit into a toolset like Quentin?"

I've considered it, but have not had time to do it as yet. (We've just completed Phase 1 of a multi-phase change out of legacy systems and I'm trying to drain the swamp right now.)

I've exchanged a couple of thoughts with Phil Factor about some other vulnerabilities and weaknesses I see in SQL Server, and I'm considering creating such a set of tools. . .
at least for my personal use. Productizing such a set of tools presents lots of challenges in their own right, so I don't know if I'll ever go that far (first and foremost I'd have to know people would pay for the tools and I have not had the time to research that.)

Maybe some day if the market isn't there I'll just post the whole lot on an 'as is' basis or, if Tony thinks they have enough merit to warrant more articles, I would be using this means of making them public.

Subject: Try Catch... hmmmm not so much
Posted by: randyvol (view profile)
Posted on: Thursday, July 17, 2008 at 12:06 PM
Message: Hi Paul -

I've not experiemented with Try Catch in SQL Server. I know this will probably start a war, but I'll say it anyway so you can understand my mind set.

I'm VERY biased when it comes to database work toward coding in SQL, period. My reason for this is early on in my career, I decided if I was going to get into SQL, then I was going to make sure I understand realtional theory and understand how to write efficient SQL.

Each RDBMS I've worked on/played with (Teradata, ORACLE, SQL Server) come with their nifty 'extensions'; but I've come to view 'extensions' as a vendor's way of saying 'this is not ANSI SQL'.

I try and code for ANSI compliance and I try to code for relational optimization. What that means is that I've developed methodologies over the years that try very hard to trap for 'everything'. Now, having said that, yes there are numerous ways that a system can go on the fritz or be put there where the code as it stands now is not going to do me much good.

Now, if there is an extension that makes goods sense to use and is not extremely intrusive (like the dbmail code), then I'll make use of it.
When it comes to writing massive blocks using CLR, or adding broader code scope with Try Catch blocks, I start to get very selective - it must provide a major benefit that I cannot think of a way to do using SQL before I'll even consider it.

I find I'm getting a little disturbed at MSFT's direction of trying to fit every language and programming approach into the "SQL Server House". Relational database programming is different than procedural programming. Making it easy for procedural programmers to code for SQL Server is just asking for trouble IMHO. As an example, I've never met a procedural developer yet that doesn't think cursors are the greatest invention ever. And I'm always finding that performance problems we have in commercial packages by and large get traced back to stored procedures and once you look inside you see a cursor there, taking one row at a time and doing extensive processing on the one row, then going back to get the next.

So, I admit it, I have a bias and "Try Catch" is something that immediately makes me think of all the VB .NET code I've written, and then I knee jerk, shudder, and move on to something that starts with DECLARE, INSERT, UPDATE, SELECT, etc. ;-)

That said, the whole reason for the existence of this code is that someone logged into the system using sa and did something really bad. First thing I had to do was make management understand that 'membership' in the sa club needed to be radically diminished. Next step, I set this trigger 'trap' to catch the next person that does this.


Subject: Historical record of change?
Posted by: randyvol (view profile)
Posted on: Thursday, July 17, 2008 at 12:12 PM
Message: Hi Reuben -

If you want to build a history table and trap the changes, then by all means feel free.

The reason I have not seen the need to do so is that on the very rare occasions this trigger has fired since bringing it into production, the email message comes right in and I know the trigger is changed.

Since I take a very dim view of ANYONE changing the status on a trigger without checking with me first, the moment I get the email, first thing I do is reverse the change. Immediately thereafter I'm on the phone to the person who did it investigating what they're up to.

(In those few instances, just the shock of me coming by and knowing what they were about has been enough to make the dust settle.)

Additionally, I have a script that I run routinely throughout the day that tells me the status of the triggers that are disabled. I know the names of all 3 that exist and are disabled, so if I ever see anything other than those 3, first thing I'll do is check the status of this trigger, then start checking how the change was made without me knowing about it... so far I have not had to do that.

Subject: Try Catch
Posted by: Paul (view profile)
Posted on: Tuesday, July 22, 2008 at 1:23 AM
Message: Hi Randy.

I value your opinion. I would not post here if I didn't. I am all for SET BASED programming (as opposed to cursor based) - I know exactly where you are comming from - It makes the world of difference.

I work in an organization where we can't get away from sql programming though - there are complete teams doing tsql development here. Scripts are rolled to dev, then uat, then uat2 and then prod. Each time tested.

There are also front-end development teams (csharp.net) and many BAs.

I was merely hoping that the Try Catch could simplify the development process. In .Net one can adjust the Try Catch's sensitivity, e.g. only watch for "these kinda" errors or "those kinda" errors, but in tsql you can't specify and yet it does not catch "all" errors.

Question is really - if you can't specify Catch "all" (which should be default), how does others handle it?

Again, Randy - if I am cross with anyone it would be Microsoft. I have sat with their people many times - giving advice, but it falls on deaf ears or people that have no say in changing things for the better.

My biggest gripe with mssql: I can see when something was created, but not when it was last used - Be it a db, record, table, field, sp, function, trigger or what ever, as a dba I want to know what can be gotten rid of. It is important to understand, I am not somuch interested in an audit trail here - I merely want to know when last something was used - huge difference. If something hasn't been used for over a year, back it up and move to file 13. I know that some 3rd party products do this to some extent, but until the day that sql merely records when last an object was used we will have this problem. We are now moving to sql 2008 and still it is not fixed. Stop giving me bells and whistles that does not work MS - Get the basics right. Why is this important - Put yourself in the shoes a dba at a large firm for a sec. You get allocated many sql servers to take care of. When you walk into such an organisation - you'll like to know from day 1 what is being used and what can be archive - no matter which company you work for. This is only possible if you can see when last any given object in the db was used (used in any way SELECT, INS, UPD, DEL, EXEC, etc).

If you've read this far - thanks for listening.

Subject: what something was last used... (and try/catch)
Posted by: randyvol (view profile)
Posted on: Tuesday, July 29, 2008 at 7:44 AM
Message: Hi Paul -

To close out on try/catch - I suppose one could take the script and embed it into a stored procedure. Then in .Net, create the connection etc. Call the sproc, and use a try/catch 'wrapper' around the call? Just something off the top of my head... been a while since I had to write a try/catch, so I'm not certain that I'm really posing a practical solution or no.

As to the last used... yep. I'm working on just such a beastie in my 'spare' time ;-). I've pretty much figured out the means to do this, I just need the time to code it up and test it.

If it works, and Tony thinks it is worthwhile, I'm sure it will find its way into this venue.
I'm hoping to have something this quarter.

Subject: Eureka - Check this out
Posted by: Paul (view profile)
Posted on: Wednesday, August 06, 2008 at 7:50 AM
Message: http://www.sql-server-performance.com/articles/dba/Capture_DDL_Changes_Using_Change_Data_Capture_p1.aspx

SQL Server 2008 introduces a new feature called Change Data Capture (CDC). CDC Captures DDL and DML activity on a SQL Server table, and places the changes in a separate SQL Server relational table...

Subject: WOO HOO !!!
Posted by: randyvol (view profile)
Posted on: Tuesday, August 19, 2008 at 6:43 AM
Message: Kuel ! Now I don't have to take more of my spare time working this issue... er, unless I don't have the $$$ to move to SQL Server 2008, or my software doesn't support SQL Server 2008, or...

But at least it is movement in the right direction !

 









Phil Factor
Bunnikins!
 When an IT manager is selected as a victim of office politics of a large corporate, it is time for him to engage in... Read more...



 View the blog
SQL Toolbelt 2008: Predominantly an Engineering Task
 The conversion of the Red-Gate tools to be compatible with SQL Server 2008 might not seem, on first... Read more...

Audit Crosschecks
 In this short article, the second of a 2-part series, William suggests a solution, using SQL Data... Read more...

Discovering Security Uses for SQL Compare
 Much of the security of SQL Server is implemented as part of the database schema. This provides some... Read more...

XML Jumpstart Workbench
 In which Robyn and Phil decide that the best way of starting to learn XML is to jump in and take a ride... Read more...

RSS Newsfeed Workbench
 Robyn and Phil decide to build an RSS newsfeed in TSQL, using the power of SQL Server's XML.  Read more...

Beginning SQL Server 2005 Reporting Services Part 1
 Steve Joubert begins an in-depth tour of SQL Server 2005 Reporting Services with a step-by-step guide... Read more...

Ten Common Database Design Mistakes
 Database design and implementation is the cornerstone of any data centric project (read 99.9% of... Read more...

SQL Server Full Text Search Language Features
 SQL Full-text Search (SQL FTS) is an optional component of SQL Server 7 and later, which allows fast... Read more...

Beginning SQL Server 2005 Reporting Services Part 2
 Continuing his in-depth tour of SQL Server 2005 Reporting Services, Steve Joubert demonstrates the most... Read more...

Executing SSIS Packages
 Nigel Rivett demonstrates how to execute all SSIS packages in a given folder using either an SSIS... Read more...

Over 150,000 Microsoft professionals subscribe to the Simple-Talk technical journal. Join today, it's fast, simple, free and secure.

Join Simple Talk