|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
Single Insert Pattern Test Harness
Single Update Pattern Test Harness
IntroductionDatabase Concurrency, a phrase that we wish would always describe a healthy state of reality instead of a problem to solve. Two interesting database concurrency problems are addressed by patterns introduced in this article:
BackgroundI love searching for patterns for reuse. Patterns make life easier, and allow us to solve new and interesting problems instead of revisiting old ones. I have pondered over two challenges for some time:
select max(identifier) from mytable
So, I thought about this for some time. Luckily, SQL2005 has some very cool functionality that makes an elegant solution to these challenges possible:
Amazing -- the two code constructs above allow us to have so much power. The first one allows SUP to be possible. The second one allows SIP to be possible. Now, let me describe these patterns in a little more detail. Allow me to present the following two patterns:
Using the codeIn order to use the code, you have to attach the SQL2005 Express database included in the attached zip file. The attached zip file contains two C# projects:
The key part of SIP pattern is the following trigger: ALTER TRIGGER .[dbo].[SingleInsertTableTrigger]
ON [dbo].[SingleInsertTable]
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON;
IF(SELECT COUNT(*) FROM SingleInsertTable) = 0
BEGIN
INSERT INTO SingleInsertTable
SELECT
Update_Time,
Operation,
ThreadId
FROM inserted
END
ELSE
BEGIN
UPDATE SingleInsertTable
SET
Update_Time = i.Update_Time,
Operation = i.Operation,
ThreadId = i.ThreadId
FROM inserted i
END
END
The key part of the SUP pattern is the following code within the -- CLAIM SOME NUMBERS - non-reset identifier
UPDATE SingleUpdateTable
SET
LatestIdentifier = LatestIdentifier + @NumberClaimed,
LatestThread = @ThreadId,
NumberClaimed = @NumberClaimed,
ClaimDate = cast(floor(CAST(GETDATE() as float)) as datetime)
OUTPUT INSERTED.* INTO @SingleUpdateTable
-- CLAIM SOME NUMBERS - identifier is reset every day
-- UNCOMMENT the following statement instead of using the one above
-- to reset the unique identifier every day.
/* UPDATE SingleUpdateTable
SET
LatestIdentifier =
case when cast(floor(CAST(GETDATE() as float)) >
ClaimDate then 1 else LatestIdentifier end
+ @NumberClaimed,
LatestThread = @ThreadId,
NumberClaimed = @NumberClaimed,
ClaimDate = cast(floor(CAST(GETDATE() as float)) as datetime)
OUTPUT INSERTED.* INTO @SingleUpdateTable
*/
The included C# projects have been written as test harnesses for the patterns introduced here. For the SingleInsertPattern test harness, you have the option of specifying the following variables:
The following tabs exist in the SingleInsertPattern test harness:
For the SingleUpdatePattern test harness, you have the option of specifying the following variables:
The following tabs exist in the SingleUpdatePattern test harness:
That's it - I hope you find these patterns as useful as I have. Points of interestThe danger of being trigger happy.... During the time when I was first writing this article, I had the opportunity to fully experience the danger of being "trigger happy". I inherited some production code that had a trigger on a table (performing fine). This table also had a trigger on that table's history table which was slowing down inserts to the main table because it was using full table scans. This took some time to track down. A SQL Profiler trace helped us spot the problem late in the evening on Friday the 13th of all days. Since SIP uses triggers ... it made me think that if you use SIP, you should SIP efficiently. :) OK, lousy pun. History
|
||||||||||||||||||||||