【问题标题】:MS SQL temporal table update failureMS SQL 时态表更新失败
【发布时间】:2017-04-02 00:55:30
【问题描述】:

我找不到任何东西来解释为什么当调用一个根据临时表上是否已经存在记录而进行插入或更新的 SP 时,我得到了

系统版本表上的数据修改失败 'MYDB.dbo.TemporalExample' 因为事务时间早于 受影响记录的期间开始时间。

这意味着什么?它似乎只在某些时候发生,我想知道是不是因为我运行多线程代码和 azure sql 只是不喜欢同一个表的相互连接,当它是一个临时表?我正在使用实体框架(最新版本),但我怀疑这是问题所在

我的就是这个

创建过程 mysp @ID 大整数, @a 浮点数, @b NVARCHAR(10), @c 十进制(19, 4) 作为 设置事务隔离级别读取未提交 设置无计数 开始尝试 如果存在(选择前 1 ID 从 my_Temporal_Table WITH (NOLOCK) 在哪里 id = @ID 和 a = @a 和 b = @b) 开始 更新 my_Temporal_Table 放 ID = @ID, a = @a, b = @b c = @c DateModified = GETUTCDATE() 在哪里 ID = @Id 结尾 别的 开始 插入 my_Temporal_Table (Id, a, b, c, DateModified) 价值观 (@ID、@a、@b、@c、GETUTCDATE()) 结尾 结束尝试 开始捕捉 声明 @ErrorMessage NVARCHAR(4000), @ErrorSeverity INT, @ErrorState 整数 选择 @ErrorMessage = ERROR_MESSAGE(), @ErrorSeverity = ERROR_SEVERITY(), @ErrorState = ERROR_STATE() -- 在 CATCH 块中使用 RAISERROR 返回错误 -- 有关导致的原始错误的信息 -- 执行跳转到 CATCH 块。 RAISERROR (@ErrorMessage, -- 消息文本。 @ErrorSeverity,-- 严重性。 @ErrorState -- 状态。 ) 结束捕获

更新 我的临时表创建脚本:

创建表 [临时]( [TemporalId] [bigint] IDENTITY(1,1) NOT NULL, [付款] [十进制](19, 4) NOT NULL, [DateModified] [datetime2](7) 非空, [SysStartTime] [datetime2](7) 始终生成为行开始不为空, [SysEndTime] [datetime2](7) 始终生成为 ROW END NOT NULL, 约束 [TemporalId] 主键集群([TemporalId] ASC) WITH(PAD_INDEX = OFF,STATISTICS_NORECOMPUTE = OFF,IGNORE_DUP_KEY = OFF,ALLOW_ROW_LOCKS = ON,ALLOW_PAGE_LOCKS = ON), SYSTEM_TIME 期间([SysStartTime],[SysEndTime]) )和( SYSTEM_VERSIONING = ON (HISTORY_TABLE = [Car2].[TemporalHistory]) )

有人可以解释为什么我会看到这个问题,它意味着什么,更重要的是我可以如何解决它?

谢谢

【问题讨论】:

  • 你为什么要更新临时表
  • 我不知道我为什么不更新它?毕竟它是一个普通的表,我没有更新历史表,它是根据 MS 文档link 仍然只是您需要更新表中的数据时使用的正常更新方法
  • 哦,好的,我以为是历史表
  • 你不使用分区吗?
  • 我为我的临时表添加了创建脚本,我没有使用分区进行更新,因为我不想将更新限制在设定的时间段内,根据 MS 文档,我应该要么?

标签: sql-server tsql azure-sql-database sql-server-2016


【解决方案1】:

所以我解决了...似乎临时表不能很好地与线程逻辑配合使用。我怀疑是因为我同时对表进行了多次并发更新;链接的历史表在更新方面滞后,以至于时间链接导致失败。使我的代码单线程解决了这个问题。临时表会受到似乎几乎是竞争条件的影响,这似乎很奇怪?我知道它不是我的代码,因为相同的代码适用于其他表。所以我想我将不得不坚持单线程逻辑,直到 MS 修复它

【讨论】:

  • 你通过显式事务调用(通过实体框架)过程?
  • 我使用支持重试策略和异步方法的天蓝色实体框架调用link
  • 你的意思是从多线程迁移到单线程,就像按顺序插入而不是同时插入一样?或者你的意思是从异步/等待模式迁移到常规的同步方法?我意识到异步/等待模式与线程有关,但只是想通过使代码单线程来澄清您的确切含义。
  • 我从并行 async/await 任务更改为 sp 的同步方法调用
【解决方案2】:

这是一种骇人听闻的解决方法,在大多数情况下并不理想,但是,如果您想在处理 sql server 时序列化对关键部分的访问,那么您可以使用内置的锁定机制通过以下方式授予对该关键部分的访问权限SP_GETAPPLOCK,不过,您可能只是根据具体情况将瓶颈转移到另一个位置。

CREATE PROC MyCriticalWork(@MyParam INT)      
AS
    DECLARE @LockRequestResult INT=0    
    DECLARE @MyTimeoutMiliseconds INT=5000--Wait only five seconds max then timeouit

    BEGIN TRAN

    EXEC @LockRequestResult=SP_GETAPPLOCK 'MyCriticalWork','Exclusive','Transaction',@MyTimeoutMiliseconds
    IF(@LockRequestResult>=0)BEGIN

            /*
            DO YOUR CRITICAL READS AND WRITES HERE
            */

        COMMIT TRAN--Releases the lock
    END ELSE
        ROLLBACK TRAN--Releases the lock  

【讨论】:

  • 你的问题是要支持天蓝色实体框架附带的异步方法。我总是可以使用单线程逻辑,但理想情况下你会想象临时表可以与异步逻辑一起使用。话虽如此,我注意到您不能在我认为很奇怪的临时表上调用 truncate,所以也许它们不支持线程等其他功能..但感谢您的建议 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多