锁定如何在 insert...select 语句中工作?

Posted

技术标签:

【中文标题】锁定如何在 insert...select 语句中工作?【英文标题】:How do locks work in a insert...select statement? 【发布时间】:2013-08-06 19:45:57 【问题描述】:

两个会话中的两个事务对同一项目进行操作:

在会话 1 中:

begin tran T1

   insert into Invoice with (item,OrderNumber) 

    select 'ItemA', max(OrderNumber)+1 

    from Orders

    where item='ItemA'

    waitfor delay '00:00:05'

commit T1

在会话 2 中:

begin tran T2

   insert into Invoice with (item,OrderNumber) 

    select 'ItemA', max(OrderNumber)+1 

    from Orders

    where item='ItemA'

commit T2

如果这样,两个相同的行将被插入到表 Orders 中。但我想在任一会话中先完成事务,然后另一个事务可以读取新的 max(OrderNumber),然后插入下一个值。我将保持锁添加到 T1 为:

begin tran T1

   insert into Invoice with (item,OrderNumber) 

    select 'ItemA', max(OrderNumber)+1 

    from Orders with (holdlock)

    where item='ItemA'

    waitfor delay '00:00:05'

commit T1

SQl SERVER 是否先将共享锁分配给 select,因为它首先解析 select 语句,然后将排他锁分配给 insert 语句?在两个会话中,锁究竟是如何相互工作的?感谢您的任何提示

【问题讨论】:

我的提示是,在任何情况下使用 (max(ordernumber)+ 1 都是一种非常糟糕的做法。如果您需要 id,请使用身份。 【参考方案1】:

您可以为您的事务使用可序列化的隔离级别。

例如:

 set transaction isolation level serializable
 begin tran
    insert into Invoice with (item,OrderNumber) 
    select 'ItemA', max(OrderNumber)+1 
    from Orders
    where item='ItemA'

    waitfor delay '00:00:05'

 commit tran 

Serializable 选项将提供以下交易功能:

    语句无法读取已被其他事务修改但尚未提交的数据 在当前事务完成之前,其他事务不能修改当前事务已读取的数据 在当前事务完成之前,其他事务无法插入键值在当前事务中的任何语句读取的键范围内的新行。

以上方法可以解决您的问题,但我建议使用标识列而不是 max ordernumber + 1 逻辑。因此,将 OrderNumber 更改为表中的标识,并在读取数据时使用 row_number 编号在运行时按 Item 计算订单编号,这是一个示例查询:

  select Item, Row_Number() over(partition by Item order by OrderNumber) as OrderNumber
  from Invoice

所以上面的查询会给出你需要的结果。

【讨论】:

【参考方案2】:

你的最终目标是什么。我认为您不能通过锁定选择来停止插入,它只会锁定所选行以防止任何更新。

【讨论】:

【参考方案3】:

数据库锁定方案是任何数据库管理应用程序的组成部分。对于存储在数据库中的数据的完整性,不同的数据库供应商提供了不同的锁定方案。 您应该检查以下链接First Link

Second Link。如果这些都没有帮助,请告诉我,以便我可以进一步帮助您。

【讨论】:

以上是关于锁定如何在 insert...select 语句中工作?的主要内容,如果未能解决你的问题,请参考以下文章

insert …select …带来的死锁问题

MYSQL 数据库基本操作

使用INSERT…SELECT语法插入记录

INSERT SELECT 语句只有一列用于 SELECT

在 INSERT SELECT 语句期间生成增量数值列值

MySQL 语句级避免重复插入—— Insert Select Not Exist