Posted on July 26, 2008 11:47 by baugustine

Most of us application developers have worked with SQL Server databases and especially tables with IDENTITY property. We have all written stored procedures to insert records into tables with columns that have IDENTITY property set.

In case you are not familiar with IDENTITY property, this property can be set on a column that has a data type of decimal, int, numeric, smallint, bigint, or tinyint. When this property is set SQL Server automatically populates that column with a next higher number in the sequence when a record is inserted. So it has been somewhat of a common practice for application developers to set the IDENTITY property for an Id column of a table and let SQL Server manage the unique primary key values. More information about IDENTITY property can be found here

Normally one of the requirement when you write a stored procedure to insert a record into a table with IDENTITY column is that you will have to pass the Id value of the newly created record back to the application. Lot of developers use the @@identity variable in SQL Server to do that. And most of the time this will work because the @@identity variable holds the last identity value generated by the insert statement. Run the sql1.sql file from the attached zip file to see this working.

So far so good. Now all that is needed for this nicely working proc to fail to return the correct Id value is to add a trigger to our table, and that trigger inserts a record into another table (say an auditing table) which also happens to have a column that has its IDENTITY property set. Run the sql2.sql file from the attached zip file to see this behavior.

So why did the incorrect id values are returned after adding the trigger. The reason is that @@identity does not have a concept of scope. It always returns the identity value of last inserted record in the database. In this case it happens to the audit table record that got inserted last. So to overcome this behavior we need to modify the proc to use the scope_identity() function to return the last identity value instead of the @@identity variable. Run the sql3.sql file from the attached zip file to see the modified proc and the correct behavior.

So always use @scope_identity() function in your insert proc to return identity column values even though you may not have triggers on your table during development. Because adding a trigger on your table is beyond your control. All it takes is for your proc and application to not work properly or even worse cause data corruption is for someone else to add a trigger on one of your tables for some reason or the other.

ScopeIdentity.zip (1.70 kb)



Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted on June 30, 2008 10:26 by lbrown

Ever wonder how that magical tag called ‘[Serializable]’ placed at the top of a class enables it to be deconstructed into XML that can be tranmitted through firewalls or persisted in a files or database tables? Not to get too technical but attribute classes are embedded into the tagged classes’ metadata at compilation and can be reflected on to perform any function you desire. 

The following example provides a real world scenario where custom attributes can enhance one’s  coding experience.  I can’t tell you how many times I have to use stored procedures that have a million parameters each having the capacity to be null.  Think of a search procedure that has ten criteria.  If I was coding this with the SqlCommand class, I would have to write a method that creates each SqlParameter and populates it with DBNull or some value. There are over ninety nine combinations in this scenario.  That really cuts into my ebay time.

If I wrote an attribute that could turn a class into a SqlCommand with populated values and could be serialized in session or view state and reduce my time coding, life would be wonderful.  In addition, if I wrote a tool that generated the code for the attributed class, my evil plan to do no work would be complete!

I have attached a solution that contains all the code mentioned above.

AtrributeSolution.zip (51.43 kb)



Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted on March 25, 2008 20:43 by mcollins

While I'm developing, I'm always making changes to code and data, and not necessarily keeping very good or accurate records in the heat of the moment about what I'm changing.  Source code is easy to resolve using modern version control tools, because I can always run a diff to see the changes that I made.  But what do I do about databases?  If I ran some ALTER statements in the database, what happens if I accidentally closed the database session and lost my changes, or if I want to go back and review what I did at the end of an iteration?  What do I do then?

Fortunately, using a trick with triggers in SQL server, I can let SQL Server keep track of the changes that I make in my database.  In my databases, the first thing that I usually create is a table for storing DDL changes to my databases and a trigger that automatically populates the table when DDL statements are executed.  By using the table and trigger, I can go back at any point and review all of the changes that were made to the database.

The table that I usually define is below:

   1: CREATE TABLE dbo.DatabaseModification (
   2:     DatabaseModificationId INT NOT NULL IDENTITY(1,1) CONSTRAINT
   3:         PKNC_DatabaseModification PRIMARY KEY NONCLUSTERED WITH
   4:         FILLFACTOR = 90 ON "default",
   5:     EventType NVARCHAR(256) NOT NULL,
   6:     PostTime DATETIME NOT NULL,
   7:     Spid INT NOT NULL,
   8:     ServerName NVARCHAR(256) NOT NULL,
   9:     LoginName NVARCHAR(256) NOT NULL,
  10:     UserName NVARCHAR(256) NOT NULL,
  11:     DatabaseName NVARCHAR(256) NOT NULL,
  12:     SchemaName NVARCHAR(256) NULL,
  13:     ObjectName NVARCHAR(256) NULL,
  14:     ObjectType NVARCHAR(256) NULL,
  15:     TSqlCommand NVARCHAR(MAX) NOT NULL,
  16:     Note NVARCHAR(MAX) NOT NULL DEFAULT N''
  17: ) ON "default";

 

Most of these fields are populated automatically from information gathered by the trigger.  The Note field was added by me.  This field allows me to annotate the changes in the database while I'm reviewing the database changes.  More and more often, I find that I'm adding this table to my database definitions and including the table in the production databases.  On my applications, I usually also create an administrator user interface for this table because I think that it's handy for a system administrator of an application to be able to look at what changes occurred to a database if something breaks or goes wrong for no apparent reason.  The administrative UI also allows the administrator to edit the contents of the Note field.

Here's the source code for the trigger that populates this table:

   1: CREATE TRIGGER trgDatabaseModificationInsert ON DATABASE FOR
   2:     DDL_DATABASE_LEVEL_EVENTS
   3: AS
   4:     SET NOCOUNT ON;
   5:     
   6:     DECLARE @data AS XML;
   7:     SET @data = EVENTDATA();
   8:     INSERT INTO dbo._zsDatabaseModification (EventType, PostTime, 
   9:         Spid, ServerName, LoginName, UserName, DatabaseName, 
  10:         SchemaName, ObjectName, ObjectType, TSqlCommand) VALUES (
  11:         @data.value('(/EVENT_INSTANCE/EventType)[1]', 
  12:             'NVARCHAR(256)'),
  13:         @data.value('(/EVENT_INSTANCE/PostTime)[1]', 'DATETIME'),
  14:         @data.value('(/EVENT_INSTANCE/SPID)[1]', 'INT'),
  15:         @data.value('(/EVENT_INSTANCE/ServerName)[1]', 
  16:             'NVARCHAR(256)'),
  17:         @data.value('(/EVENT_INSTANCE/LoginName)[1]', 
  18:             'NVARCHAR(256)'),
  19:         @data.value('(/EVENT_INSTANCE/UserName)[1]', 
  20:             'NVARCHAR(256)'),
  21:         @data.value('(/EVENT_INSTANCE/DatabaseName)[1]', 
  22:             'NVARCHAR(256)'),
  23:         @data.value('(/EVENT_INSTANCE/SchemaName)[1]', 
  24:             'NVARCHAR(256)'),
  25:         @data.value('(/EVENT_INSTANCE/ObjectName)[1]', 
  26:             'NVARCHAR(256)'),
  27:         @data.value('(/EVENT_INSTANCE/ObjectType)[1]', 
  28:             'NVARCHAR(256)'),
  29:         @data.value('(/EVENT_INSTANCE/TSQLCommand)[1]', 
  30:             'NVARCHAR(MAX)'));
  31:     
  32:     SET NOCOUNT OFF;
  33: GO

 

By creating this trigger for DDL_DATABASE_LEVEL_EVENTS, any DDL statements that would modify the database by creating, altering, or deleting database objects will get logged in the DatabaseModification table.

Technorati tags: , , , ,


Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkList

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5