【问题标题】:What type of lock (table hint) to use for this?为此使用什么类型的锁(表提示)?
【发布时间】:2024-01-20 18:26:01
【问题描述】:

假设我有两列 CategoryId 和 SubcategoryId。对于每个 CategoryId,SubcategoryId 从 1 开始。插入新的子类别时,我需要为给定的 CategoryId 计算 MAX(SubcategoryId)+1。

下列说法正确吗?

BEGIN TRANSACTION
DECLARE @NextId bigint;
SELECT @NextId = ISNULL(MAX(SubcategoryId),0) + 1 
FROM Subcategory WITH (UPDLOCK, HOLDLOCK) 
WHERE CategoryId = @CategoryId;
INSERT INTO Subcategory
(
    CategoryId, 
    SubcategoryId
) 
VALUES
(
    @CategoryId, 
    @NextId
 );
COMMIT

WITH(UPDLOCK, HOLDLOCK) 是否也防止并发插入具有相同 CategoryId 的新行,还是仅锁定在 SELECT 时匹配的现有行?如果是后者,我应该将其更改为 TABLOCK 吗?是否也需要某种排他锁?

【问题讨论】:

  • 能否请您发布表格定义。这会很有帮助,因为您可能不需要这样做,只需一个简单的身份。
  • 表格很简单。 Subcategory(CategoryId bigint, SubcategoryId bigint) 和由两列组成的唯一键,以便 SubcategoryId 可以针对不同的 CategoryId 重复。
  • 为您添加了一个可能的答案。
  • 我最初在上面发布的代码是正确的解决方案。 UPDLOCK、HOLDLOCK 防止插入任何具有相同 CategoryId 的新记录。

标签: sql concurrency locking rdbms


【解决方案1】:

一般来说,除非您有非常特殊的要求,否则不建议搞乱锁定级别。据我了解,有一种方法可以让您的并发问题由 SQL 服务器引擎处理而无需锁定提示。

所以你的表定义是:

CREATE TABLE Subcategory
(
   CategoryId bigint, 
   SubcategoryId bigint
 )

如果将表定义更改为:

CREATE TABLE Subcategory
(
   CategoryId bigint NOT NULL, 
   SubcategoryId bigint IDENTITY(1,1) NOT NULL
 ) ON [PRIMARY];
 ALTER TABLE Subcategory
 ADD CONSTRAINT pk_Subcategory PRIMARY KEY (CategoryId,SubcategoryId  )

SubcategoryID 将成为自动编号。这个数字将在每次插入时递增。因此,您不需要在插入期间指定子类别 ID,这将允许 SQL Server 处理并发性。最好让SQL处理表的锁定和并发处理。

如果您将表结构更改为我建议的结构,您的查询也会变得更简单。

BEGIN TRAN
INSERT INTO Subcategory
(
    CategoryId, 

) 
VALUES
(
    @CategoryId, 

 );
 COMMIT TRAN

这可能是您的解决方案。抱歉,我的语法可能有误,因为我已经好几个月没有接触过 SQL 服务器了。

【讨论】:

  • CategoryId 和 SubcategoryId 都已经是生成并存储在其他主表中的标识值。此表只是将子类别分组到类别下。在进一步阅读了各种锁定之后,我确定我最初的建议是唯一完全安全的解决方案。
最近更新 更多