没有行但是(XLOCK,ROWLOCK)锁定了吗?

时间:2011-08-09 05:36:13

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

考虑这个简单的表格: enter image description here

表create语句是:

CREATE TABLE [dbo].[Test_Serializable](
[Id] [int] NOT NULL,
[Name] [nvarchar](50) NOT NULL
)

所以没有任何主键或索引。

认为它是emopty并且没有任何行。我想要插入此行(1,'nima')但我想检查是否有一行Id=1或者没有。如果是,请致电RAISERROR如果没有插入行。我写这个脚本:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRY
BEGIN TRAN ins

    IF EXISTS(SELECT * FROM Test_Serializable ts WITH(xlock,ROWLOCK) WHERE ts.Id=1)
        RAISERROR(N'Row Exists',16,1);


    INSERT INTO Test_Serializable
    (
        Id,
        [Name]
    )
    VALUES
    (
        1,
        'nima'
    )
   COMMIT TRAN ins
     END TRY
     BEGIN CATCH
        DECLARE @a  NVARCHAR(1000);
        SET @a=ERROR_MESSAGE();
        ROLLBACK TRAN ins
        RAISERROR(@a,16,1);
     END CATCH

这个脚本运行正常,但有一点很有趣。

我从2个SSMS运行这个脚本并逐步运行这2个脚本(在调试模式下)。但是,有趣的是,当到达IF EXIST语句锁定表时,我的表没有行但只有一个脚本。

我的问题是(XLOCK,ROWLOCK)是否锁定整个表,因为没有行?或者锁定幻像行:) !! ???

编辑1)

这是我的情景:

我有一个表格,例如6个字段

enter image description here

这是唯一性规则:

1)City_Code + F1_Code是唯一的

2)City_Code + F2_Code是唯一的

3)City_Code + F3_Code + F4_Code是uinque

问题是用户可能想要填充city_codeF1_Code,当它想要将其插入其他文件时,我们必须Empty String0(对于数字字段)值。

如果用户想要填充City_Code + F3_Code + F4_Code,则F1_Code和F2_Code必须具有Empty String

如何更好地检查?我无法为每个规则创建任何唯一索引

2 个答案:

答案 0 :(得分:5)

要回答您的问题,SERIALIZABLE隔离级别会执行范围锁定,其中包括范围内不存在的行。

http://msdn.microsoft.com/en-us/library/ms191272.aspx

  

键范围锁定可确保执行以下操作   序列化:

     

范围扫描查询

     

单点提取不存在的行

     

删除操作

     

插入操作

答案 1 :(得分:4)

XLOCK是独占锁:因此当WHERE遍历行时,行被锁定。

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE不是关于行的重复或锁定,它只是删除"Phantom reads"的机会。从锁定的角度来看,它需要范围锁(例如A和B之间的所有行)

因此,使用XLOCK和SERIALIZABLE,您可以锁定表格。你想要的UPDLOCK不是唯一的。

使用UPDLOCK,此模式不安全。在高负载下,您仍然会遇到重复错误,因为2个并发EXISTS将找不到行,两者都尝试INSERT,一个会出现重复错误。

因此,只需尝试INSERT并捕获错误:

BEGIN TRAN ins
    INSERT INTO Test_Serializable
    (
        Id,
        [Name]
    )
    VALUES
    (
        1,
        'nima'
    )
   COMMIT TRAN ins
END TRY
BEGIN CATCH
   DECLARE @a  NVARCHAR(1000);
   IF ERROR_NUMBER() = 2627
     RAISERROR(N'Row Exists',16,1);
   ELSE
   BEGIN
     SET @a=ERROR_MESSAGE();
     RAISERROR(@a,16,1);
   END
   ROLLBACK TRAN ins
END CATCH

我已经提到了before

编辑:强制SQL Server 2008的各种唯一身份用户

使用过滤的索引

CREATE UNIQUE NONCLUSTERED INDEX IX_UniqueF1 ON (City_Code, F1_Code)
   WHERE F2_Code = '' AND F3_Code = '' AND AND F4_Code = 0;

CREATE UNIQUE NONCLUSTERED INDEX IX_UniqueF1 ON (City_Code, F2_Code)
   WHERE F1_Code = '' AND F3_Code = '' AND AND F4_Code = 0;

CREATE UNIQUE NONCLUSTERED INDEX IX_UniqueF3F4 ON (City_Code, F3_Code, F4_Code)
   WHERE F1_Code = '' AND F2_Code = '';

您可以对早期版本的索引视图执行相同的操作