SQL Server Triggers containing Cursors (AKA: how *NOT* to audit)

I’m working with a client at the moment who has a lot of legacy code.

One of the things that makes the systems they’ve got so bad is that there’s little thought of performance when things have been written. An excellent example is the use of multiple nested cursors within triggers in SQL Server.

Obviously at some point a developer was given a requirement for auditting. As not all of of the tables (nor every column in a table) was required to be auditted they thought to themselves that triggers using cursors would be an excellent plan.

So, they created a table called “audit” that contained a timestamp; user id and name that caused the change, the source row id, table and column; and before and after values.

Now i’ve no doubt that this is a nice scheme for quickly showing a list of changes on a screen (i.e. select * from audit where sourcetable = ‘address’ and sourcetableid = ‘’ order by changedate DESC) but this is a performance killer if you ever have much concurrency and you update things in batch.

To get the data within the trigger to build the audit info you need to encode the table name, look up the username (for the user that made the change - as that was denormalised into this table too - ironically no doubt to speed up read performance), then iterate around another table that tells you which columns you’re interested in auditting, for each of those check if they’ve changed (in a cursor of course), then find the differences and do an insert for each.

So, if we’ve got 8 columns to change then we’ve got one trigger, one lookup for the user’s username, one cursor iterating around 8 “columns to audit” entries, at least 8 lookups against before and after data in the trigger, then up to 8 inserts into the audit table.

Now, some people are thinking… what’s the problem?

Firstly, one of volume: the client mentioned has a few million rows and does updates that can sometimes effect 10s of thousands of rows, suddenly a trigger per row hurts.

Secondly, they’ve obviously been looking at performance the wrong way - they have about 1000 users (and the table for users is the ntusername, vs the userid - not exactly large - so it’s easy for sql server to cache in tempdb as it’s used all the time). Next up the audit data isn’t going to change on a day by day basis - so why not generate that list of columns to check straight into the trigger code? At least that way you’ve skipped another table lookup.

I’m guessing you’re thinking - so what’s the right way? what’s the magic bullet/golden hammer? - There isn’t one, for every situation there’s a different solution, but it all starts by understanding the way your application needs to work.

I use two basic practises a lot;

a) Never update a row - have a timestamp against a row and the user id that created it and a marker that it’s historical. when you need to change data simply insert a new row (with the Now timestamp and the current user’s id) with the new data, then mark the original row as defunct. You either get the history live by showing before and afters, or have a batch job that turns it into user friendly stuff in a downtime (if one exists). If you use Stored Procedures/SPs (i’m assuming you do!) then it’s easy to implement this scheme in there.
b) Generate a table that matches the schema of the original table you wish to audit and prefix the name with audit or history (potentially you can trim down the columns in there if you’re not interested in all of them) and then insert the original data into the audit table whenever you go to update it. Again this is easy to do in the Stored Procs/SPs.

In general (a) is the simplest to implement but it’s only really usable if you have smallish data sets (especially if you put a clustered key on the “defunct” marker column - as that update will require a table reshuffle). (b) is more work but at least you’re segmenting your data into historical vs. current - so any stupid programmer mistakes (such as not using the defunct marker in a select query on the data can return a history row instead and confuse the user).

74 Responses to “SQL Server Triggers containing Cursors (AKA: how *NOT* to audit)”

  1. Zyrtec Says:

    good article

  2. Clomid Says:

    Thank you. What you need))

  3. Narkoman Says:

    comment…

    Приятно читать блог…

  4. robes Says:

    cool story.

    robe de soirée

  5. Julianna Says:

    Howdy, i read your blog often and i own an analogous one and i used to be simply wondering if you get a lot of spam comments? If thus how do you stop it, any plugin or anything you’ll be able to advise? I get thus much lately it’s driving me mad so any help is very abundant appreciated.

  6. Aaliyah Says:

    I admire the valuable information you offer in your articles. I will bookmark your blog and have my children check up here often. I am quite sure they will learn lots of new stuff here than anybody else!

  7. Romeo Says:

    You made fantastic nice points here. I performed a search on the issue and discovered almost all peoples will agree with your blog.

  8. Colten Says:

    You made tremendous great ideas here. I done a research on the subject and learnt most peoples will agree with your blog.

  9. robes Says:

    awesome story.

    robe noire

  10. Jaslene Says:

    You had some good ideas there. I made a search on the issue and noticed most peoples will agree with your blog.

  11. Pottery Says:

    Valuable thoughts and advices. I read your topic with great interest.

  12. Renting Property Says:

    Aw, this was a really quality post. In theory I’d like to write like this too - taking time and real effort to make a good article… but what can I say… I procrastinate alot and never seem to get something done.

  13. size Says:

    This is a really good read for me, Must admit that you are one of the best bloggers I ever saw.Thanks for posting this informative article.

  14. Kailey Says:

    I found your web page from bing and it is superb. Thankx for providing such an informative post!!!!

  15. sizegenetics Says:

    This a little bit funny. I found your site via search engine a few moment ago, and luckily, this is the only information I was looking for the last hours.

  16. $39 watches Says:

    fake watch

  17. watch replicas Says:

    replica watches

  18. cheap watches Says:

    replica watches

  19. Liam Says:

    You had great positive ideas there. I did a search on the issue and found nearly all peoples will agree with your blog.

  20. gotsocialmedia Says:

    I liked it.

  21. Beau Says:

    Hi there, I found your blog on Google while seeking for first aid for a heart attack and your post looks very interesting for me.

  22. fake watch Says:

    replica watch

  23. fake watches Says:

    fake watches

  24. $49 watches Says:

    rolex air king watches

  25. breitling watch Says:

    fake bvlgari

  26. tag heuer Says:

    Louis Vuitton Handbags

  27. tag heuer watch Says:

    replica handbags

  28. rolex replica Says:

    omega watches

  29. cartier watches Says:

    daytona watches

  30. replica watches Says:

    bell ross

  31. guess watch Says:

    fake breguet

  32. replica rolex Says:

    fake bell ross

  33. dkny watches Says:

    breitling

  34. invicta watches Says:

    omega

  35. rolex watches Says:

    relica cartier watches

  36. Alex Says:

    < a href=”http://avto-himki.ru/ yjeuaoy@auixech.ru” >1< / a >…

    no more…

  37. tag heuer Says:

    replica watches

  38. replica rolex Says:

    fake watches

  39. replica rolex Says:

    watchvisa

  40. swiss watches Says:

    fake watch

  41. fake bags Says:

    fake cartier

  42. replica rolex Says:

    louis vuitton

  43. rolex Says:

    replica watch

  44. dkny watches Says:

    replica watches

  45. watches Says:

    replica watches

  46. replica watches for sale Says:

    rolex

  47. http://community.beliefnet.com/rickdcurry82/blog/2010/07/23/competitive_business_model Says:

    Thank you very much for that great article We’re down to triple digits for the first time since we’ve been together….

    Interesting and informative. But will you write about this one more? Buy Cheap Electronic Gadgets Online…

  48. Dolce & Gabbana Says:

    handbags sale

  49. Sonny Wyse Says:

    You gave honest ideas there. I did a search on the issue and got most peoples will agree with your blog. It’s always a good idea to ask what people thought the book was about, and how this was conveyed.

  50. replica watches Says:

    replica designer watches

  51. replica Says:

    omega

  52. tag heuer watches Says:

    swiss watches store

  53. replica watches Says:

    women’s watches

  54. replica Says:

    uggs

  55. cartier watches Says:

    ugg

  56. rolex watches Says:

    swiss replica watches

  57. breitling watch Says:

    replica

  58. omega watches Says:

    replica louis vuitton

  59. breitling watch Says:

    ugg boot

  60. replica designer watches Says:

    omega watches

  61. gloves Says:

    small handbags

  62. citizen watch Says:

    watch on sale

  63. watches Says:

    ugg boots

  64. Designer Handbags Says:

    lv

  65. wholesale watches Says:

    longines

  66. swiss watches Says:

    replica watches, replica watches

  67. omega watches Says:

    replica watch, replica watch

  68. gold keeper ring Says:

    buckle watches

  69. replica watches Says:

    patek philippe

  70. Designer Handbags Says:

    Fake Handbag

  71. ugg boots for sale Says:

    ugg boot

  72. replica watches Says:

    ugg, ugg

  73. swiss watches Says:

    replica watch, replica watch

  74. breitling watch Says:

    replica handbags,replica handbags

Leave a Reply