我对锁和提示相当新。
我有一张频繁SELECT
和INSERT
操作的表格。该表有1100万条记录。
我已经添加了一个新列,我需要将同一个表中现有列的数据复制到新列。
我打算使用ROWLOCK
提示来避免将锁升级到表级锁并阻止表上的所有其他操作。例如:
UPDATE
SomeTable WITH (ROWLOCK)
SET
NewColumn = OldColumn
问题:
NOLOCK
代替ROWLOCK
吗?请注意,一旦将记录插入表中,OldColumn的值就不会更改,因此NOLOCK
不会导致脏读。NOLOCK
是否有意义,因为SQL Server必须获得UPDATE
的更新锁。我知道要避免使用提示,SQL Server通常会做出更明智的选择,但我不希望在此次更新期间将表锁定。
答案 0 :(得分:4)
尝试批量更新。
DECLARE @Batch INT = 1000
DECLARE @Rowcount INT = @Batch
WHILE @Rowcount > 0
BEGIN
;WITH CTE AS
(
SELECT TOP (@Batch) NewColumn,OldColumn
FROM SomeTable
WHERE NewColumn <> OldColumn
OR (NewColumn IS NULL AND OldColumn IS NOT NULL)
)
UPDATE cte
SET NewColumn = OldColumn;
SET @Rowcount = @@ROWCOUNT
END
答案 1 :(得分:2)
我采用了@ pacreely的方法(参见他对这个问题的回答)批量更新并创建了update...top
变体。我添加了(rowlock)提示告诉SQL服务器将锁保持在行级别。
有关详细信息,请参阅update...top。另请注意,在order by
,top
,update
,insert
语句中使用merge
时,您无法使用delete
,因此引用的行不会按任何顺序排列。
declare @BatchSize int = 1000
declare @RowCount int = @BatchSize
while @RowCount > 0
begin
update top (@BatchSize) SomeTable with (rowlock)
set NewColumn = OldColumn
where
NewColumn <> OldColumn or
(
NewColumn is null and
OldColumn is not null
)
select @RowCount = @@rowcount
end
答案 2 :(得分:1)
此问题在StackExchange上的数据库管理员站点上有一个答案:https://dba.stackexchange.com/questions/127158/how-to-get-sql-insert-and-or-update-to-not-lock-entire-table-on-ms-sql-server
答案 3 :(得分:0)
最近,我们有一个案例,我们想做类似的事情,但是要花几天时间(每次运行只能更新一定数量的记录,并且只能在特定时间内更新)。最近的数据很好,但是需要更新数百万行的旧数据。我们的数据表如下所示:
Create Table FileContent
(
FileContent varchar(max),
File_PK bigint,
NewFileContent varchar(max)
)
我们只需要更新某些行,但需要更新数百万行。我们创建了一个表来存储进度,以便我们可以使用计划的作业来迭代并更新主表,然后用需要更新的主表记录的主键填充该表:
Create Table FilesToUpdate
(
File_PK bigint,
IsUpdated bit NOT NULL DEFAULT 0
)
然后,我们安排了以下脚本来进行更新(供您自己使用,请根据您的系统的工作情况使用批处理大小和安排时间)。
/***
Script to update and fix records.
***/
DECLARE @Rowcount INT = 1 --
, @BatchSize INT = 100 -- how many rows will be updated on each iteration of the loop
, @BatchesToRun INT = 25 -- the max number of times the loop will iterate
, @StartingRecord BIGINT = 1;
-- Get the highest File_PK not already fixed as a starting point.
Select @StartingRecord = MAX(File_PK) From FilesToUpdate where IsUpdated = 0
-- While there are still rows to update and we haven't hit our limit on iterations...
WHILE (@Rowcount > 0 and @BatchesToRun > 0)
BEGIN
print Concat('StartingRecord (Start of Loop): ', @StartingRecord)
UPDATE FileContent SET NewFileContent = 'New value here'
WHERE File_PK BETWEEN (@StartingRecord - @BatchSize + 1) AND @StartingRecord;
-- @@Rowcount is the number of records affected by the last statement. If this returns 0, the loop will stop because we've run out of things to update.
SET @Rowcount = @@ROWCOUNT;
print Concat('RowCount: ', @Rowcount)
-- Record which PKs were updated so we know where to start next time around.
UPDATE FilesToUpdate Set IsUpdated = 1 where File_PK BETWEEN (@StartingRecord - @BatchSize + 1) AND @StartingRecord;
-- The loop will stop after @BatchSize*@BatchesToRun records are updated.
-- If there aren't that many records left to update, the @Rowcount checks will stop it.
SELECT @BatchesToRun = @BatchesToRun - 1
print Concat('Batches Remaining: ',@BatchesToRun)
-- Set the starting record for the next time through the loop.
SELECT @StartingRecord -= @BatchSize
print Concat('StartingRecord (End of Loop): ', @StartingRecord)
END