同时运行多个执行时的sql语句问题

时间:2011-09-07 21:00:51

标签: sql-server-2005 sql-server-2008

问题是每当我在同时查询Windows时执行存储过程“ usp_Execute100K ”时,服务器无法找到记录并创建新记录,即使记录已经存在。您必须同时运行“usp_Execute100K”多个窗口以查看此问题。

我想要的是确保服务器每天只创建1条记录,如果记录确实存在,则只将“StatisticTotal”列更新为下一个数字。

教会这个问题。

以下是1个表和两个存储过程

统计/ 表名 /

usp_Statistic_InsertOrUpdate / 这将更新“StatisticTotal”列,或者如果记录不存在则插入 /

usp_Execute100K / *这将执行usp_Statistic_InsertOrUpdate 10万次* /

请复制下面的脚本以重新制作桌子和存储的程序。

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

2 个答案:

答案 0 :(得分:1)

1)在Statistic.StatisticAccount + Statistic.StatisticCreatedDate上创建一个唯一索引,或仅在Statistic.StatisticCreatedDate创建您的业务规则所指示的内容。

2)用你的INSERT包装:

BEGIN TRY
    INSERT ...
END TRY
BEGIN CATCH
END CATCH

它只会忽略/丢弃失败的重复插入所产生的错误。

答案 1 :(得分:1)

您是否考虑过在事务中包装UPDATE / INSERT组合?虽然约束和TRY / CATCH肯定会阻止第二行,但它也会丢弃该尝试。如果你把它包装在一个事务中,并使用TABLOCK(我假设你可能会对这个表进行大量的写操作,但是没有大量的读操作),你会稍微妨碍并发性,但你可以更好地确保一个用户只能看到之后表的状态,其他用户完成了它。我可以原样可靠地获得两行,但是当我使用它时我无法获得两行:

ALTER PROCEDURE [dbo].[usp_Statistic_InsertOrUpdate]
    @StatisticAccount UNIQUEIDENTIFIER
AS
BEGIN
    SET NOCOUNT ON; -- this should always be on

    DECLARE @NowDate DATE = CURRENT_TIMESTAMP; 
    -- by definition, you don't need to convert to remove time from date

    BEGIN TRANSACTION;

    --UPDATE ONLY IF IT HAS THE SAME DATE
    UPDATE dbo.Statistic WITH (TABLOCK)
      SET 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 dbo.Statistic
        (
          StatisticID,
          StatisticAccount,
          StatisticTotal,
          StatisticCreatedDate,
          DebugDate
        )
        SELECT
          NEWID(),
          @StatisticAccount,
          1,
          @NowDate,
          @NowDate;
    END

    COMMIT TRANSACTION;
END
GO

错误捕获,回滚,约束等。我将作为练习离开。