【发布时间】:2014-10-02 09:20:03
【问题描述】:
我知道默认情况下 SQL 语句不会异步执行,但我遇到的情况似乎是这样的。
表格
[#data][tbl_Bucket][tbl_IDPool]
程序
[sp_InsertIntoBucket][sp_GenerateID][sp_UpdateIDPool]
流程
- 一个应用程序调用
[sp_InsertIntoBucket] -
[sp_InsertIntoBucket]致电[sp_GenerateID] -
[sp_GenerateID]查询[tbl_IDPool]并生成一个值 -
[sp_GenerateID]致电[sp_UpdateIDPool] -
[sp_UpdateIDPool]写信给[tbl_IDPool] -
[sp_GenerateID]将其生成的值返回给[sp_InsertIntoBucket] -
[sp_InsertIntoBucket]使用该值作为[tbl_Bucket]中新记录的主键 -
[sp_InsertIntoBucket]将生成的值返回给调用者
场景
[#data] 具有发往[tbl_Bucket] 的信息(1500 - 12000 条记录)。由于 [sp_InsertIntoBucket] 一次只能处理一条记录,因此该过程是 RBAR 的 - 对于 [#data] 中的每条记录,都会调用 [sp_InsertIntoBucket]。
问题
[sp_GenerateID] 生成重复值。在[sp_InsertIntoBucket] 中的实际INSERT 发生并引发错误之前,我已经有13 到130 个重复的生成值。
生成的值取决于[tbl_IDPool] 中的数据,因此对于每个[sp_GenerateID] 调用调用[sp_UpdateIDPool] 以确保下一个 [sp_GenerateID] 调用生成一个独特的价值。
我怀疑这与[sp_GenerateID] 在[sp_UpdateIDPool] 完成对[tbl_IDPool] 的写入之前被第二次调用有关。但这没有任何意义,因为 RBAR 应该等待 [sp_InsertIntoBucket],后者应该等待 [sp_GenerateID],后者应该等待 [sp_UpdateIDPool],然后再移动到下一个 [#data] 条目,对吧?
我的尝试
-
WAITFOR DELAY "00:00:00.003"- 这行得通,但我正在寻找更好、更高效、更优雅的解决方案。 -
WHILE与CURSOR- 唯一的区别是CURSOR稍慢。 - 在
[tbl_IDPool]的[sp_GenerateID]查询中使用和不使用WITH (NOLOCK)希望写入(第一次调用)会锁定读取(第二次调用)。
【问题讨论】:
-
您应该避免在存储过程名称的开头使用
sp_。文档中有警告说这是为 Microsoft 的 system 程序保留的,如果存在名称冲突(现在,或者可能以后系统更新),Microsoft 将“获胜”。 -
@Damien_The_Unbeliever 这很有趣。感谢您的提醒。我没有意识到这一点,并且将来会这样做 - 或者我应该说不这样做。然而,在这种情况下,它只是为了区分实体类型。您的警告已被记录。
-
不同事务之间有并发,对吧?为什么您认为存在事务内异步?竞争条件似乎很好地解释了这种行为。
-
感谢您抽出宝贵时间;感谢您的意见。我没想到 SQL Server 会以这种方式运行,因为 RBAR 循环正在向所有其他过程启动“调用堆栈”。它 [顶级的外部会话] 不能与自身并发,可以吗?也许我没有正确理解你。
-
对 SQL Server 的单个查询永远不会创建并发(如果您确实想要并行性,这会很烦人)。当您调用 proc 时,该调用是同步和阻塞的。我很确定你是否说过,但很可能有多个事务(在不同的会话中)在这里对相同的数据进行操作。尝试答案中的 SERIALIZABLE 建议来测试该理论。 SERIALIZABLE 提供了单线程执行的假象。
标签: sql-server tsql stored-procedures rbar