The problem is whenever I execute the stored procedured “usp_Execute100K” in the Query Windows simultaneously the server could not locate the record and it creating a new record even though the record is already there. You have to run the “usp_Execute100K” multiple windows simultaneously to see this problem.
What I want is to make sure the server only create 1 record per day, if the record does exist then only update the “StatisticTotal” column to the next number.
This problem is taught.
The below are 1 table and two stored procedures
Statistic /Table name/
usp_Statistic_InsertOrUpdate /this will update the “StatisticTotal” column or insert if the record does not exist/
usp_Execute100K /* this will execute the usp_Statistic_InsertOrUpdate 100 thousand times*/
PLEASE COPY THE SCRIPT BELOW TO RECREATE THE TABLE AND STORED PROCEDURES.
GO
/****** Object: StoredProcedure [dbo].[usp_Execute100K] Script Date: 09/07/2011 15:37:29 ******/
DROP PROCEDURE [dbo].[usp_Execute100K]
GO
/****** Object: StoredProcedure [dbo].[usp_Statistic_InsertOrUpdate] Script Date: 09/07/2011 15:37:29 ******/
DROP PROCEDURE [dbo].[usp_Statistic_InsertOrUpdate]
GO
/****** Object: Table [dbo].[Statistic] Script Date: 09/07/2011 15:37:28 ******/
DROP TABLE [dbo].[Statistic]
GO
/****** Object: Table [dbo].[Statistic] Script Date: 09/07/2011 15:37:28 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Statistic](
[StatisticID] [uniqueidentifier] NOT NULL,
[StatisticAccount] [uniqueidentifier] NOT NULL,
[StatisticTotal] [float] NOT NULL,
[StatisticCreatedDate] [datetime] NOT NULL,
[DebugDate] [datetime] NULL,
CONSTRAINT [PK_Statistics] PRIMARY KEY CLUSTERED
(
[StatisticID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
/****** Object: StoredProcedure [dbo].[usp_Statistic_InsertOrUpdate] Script Date: 09/07/2011 15:37:29 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[usp_Statistic_InsertOrUpdate](
@StatisticAccount uniqueidentifier
)
AS
SET NOCOUNT OFF; --this can be turn on or off, it has no effects
DECLARE @NowDate date
SET @NowDate=CONVERT(datetime, CONVERT(char, GETDATE(), 106))--remove all time.
--UPDATE ONLY IF IT HAS THE SAME DATE
UPDATE TOP (1) Statistic SET StatisticTotal =StatisticTotal+1 WHERE (StatisticAccount=@StatisticAccount AND StatisticCreatedDate = @NowDate)
if @@ROWCOUNT=0--If the above statement return no effects then create a new record
BEGIN
INSERT TOP (1)INTO Statistic(StatisticID, StatisticAccount, StatisticTotal, StatisticCreatedDate,DebugDate) VALUES (NEWID(),@StatisticAccount,1,@NowDate,@NowDate)
END
GO
/****** Object: StoredProcedure [dbo].[usp_Execute100K] Script Date: 09/07/2011 15:37:29 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[usp_Execute100K]
AS
SET NOCOUNT OFF; --this can be turn on or off, it has no effects
declare @current float
set @current=0
while (@current <100000)
begin
--INSERT THIS STATEMENT for 100 thousand times
exec usp_Statistic_InsertOrUpdate'4c34eea5-fe17-4b11-8e06-0039577e7421'
set @current = @current + 1
end
GO
Have you considered wrapping your UPDATE/INSERT combo in a transaction? While the constraint and TRY/CATCH will certainly prevent a second row, it will discard the attempt as well. If you wrap it in a transaction, and use TABLOCK (I assume you are potentially doing a lot of writes to this table, but not a ton of reads), you hamper concurrency slightly but you can better ensure that one user will only see the state of the table after the other user is done with it. I could get two rows reliably as is, but I couldn’t get two rows when I used this:
Error trapping, rollback, constraints etc. I’ll leave as an exercise.