IF OBJECT_ID('Example')>0
DROP TABLE Example;
GO
CREATE TABLE [dbo].[Example]
(
[SaleID] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY,
[Product] [char](150) NULL,
[SaleDate] [datetime] NULL,
[SalePrice] [money] NULL
)
GO
DECLARE @i SMALLINT
SET @i = 1
WHILE (@i <=100)
BEGIN
INSERT INTO Example
(Product, SaleDate, SalePrice)
VALUES
('Computer', DATEADD(mm, @i, '3/11/1919'), DATEPART(ms, GETDATE()) + (@i + 57))
INSERT INTO Example
(Product, SaleDate, SalePrice)
VALUES
('BigScreen', DATEADD(mm, @i, '3/11/1927'), DATEPART(ms, GETDATE()) + (@i + 13))
INSERT INTO Example
(Product, SaleDate, SalePrice)
VALUES
('PoolTable', DATEADD(mm, @i, '3/11/1908'), DATEPART(ms, GETDATE()) + (@i + 29))
SET @i = @i + 1
END
GO
此时我们再来插入一条测试数据:
BEGIN TRANSACTION
INSERT INTO Example
(Product, SaleDate, SalePrice)
VALUES
('PoolTable', GETDATE(), 500)
此时我们保持该事务窗口打开,所以此时在表中仍然会记录着对其所发出的锁,接下来我们在另外一个窗口查询表中数据总行数并使用NOLOCK提示。
BEGIN TRANSACTION
INSERT INTO Example
(Product, SaleDate, SalePrice)
VALUES
('PoolTable', GETDATE(), 500)
ROLLBACK TRANSACTION
此时我们回滚了之前插入的数据,我们再来利用NOLOCK提示来查询数据总函数。
BEGIN TRANSACTION
UPDATE TOP(1) Example
SET SalePrice = SalePrice + 1
由于我们并未提交或者回滚事务所以此时更新的数据行已经被影响,下面我们利用READPAST提示来查询表中总数据行。
SELECT COUNT(*)
FROM Example WITH(READPAST)
在我们的测试表中数据行为300条,同时我们进行了上述更新,当我们利用READPAST提示进行查询总数据行时,因为更新而未提交或者回滚导致此时有一行记录被排它锁锁住,而READPAST的作用则是跳过锁住的行,所以此时很明显只返回299条数据,如下:
DECLARE @Next INTEGER
BEGIN TRANSACTION
-- 找到下一个满足条件的值
SELECT TOP 1 @Next = Id
FROM Test WITH (UPDLOCK, READPAST)
WHERE Flag = 0
ORDER BY Id ASC
--若找到利用标识更新,防止下一次被读取到
IF (@Next IS NOT NULL)
BEGIN
UPDATE Test
SET Flag = 1
WHERE Id = @Next
END
COMMIT TRANSACTION
-- 返回我们查询到的值
IF (@Next IS NOT NULL)
SELECT * FROM Test WHERE Id = @Next
当然上述可以避免阻塞,我们也可以在阻塞的情况下来处理利用ROWLOCK和HOLDLOCK来解决
BEGIN TRAN
SELECT
FROM Test
WITH (HOLDLOCK, ROWLOCK)
WHERE Id = 1
--TODO