我需要从最近正在写入的表中删除旧记录。我的问题是,当其他进程尝试读取或写入表时,我的delete语句将因死锁而失败。
它是一个大型存储过程的一部分,该存储过程以Transaction isolation level read uncommitted
和deadlockpriority low
开头。
如何转换此cte和delete语句以部分删除记录?例如,如果我可以选择在cte(x)中为x / 500条记录运行delete语句的记录数,那会很完美。
;with chargesToDelete(id, ciid) as (
select c.id, ci.Id from @chargeids c
left join xxx.dbo.chargeitems ci on ci.Charge_Id = c.id
where ci.id is null
)
delete from xxx.dbo.charges
where Id in (select id from chargesToDelete);
答案 0 :(得分:1)
您可以在@@ROWCOUNT
上使用会话变量TOP N
和DELETE
进行循环。
SELECT 1 -- Forces @@ROWCOUNT = 1
WHILE @@ROWCOUNT > 0
BEGIN
delete TOP (500) I from
xxx.dbo.charges AS I
where
exists (
select
'to delete'
from
@chargeids c
left join xxx.dbo.chargeitems ci on ci.Charge_Id = c.id
where
ci.id is null AND
c.id = I.Id)
END
如果您不想使用前哨SELECT
(它将结果返回给客户端,如果有的话),则可以使用变量。
DECLARE @ForceStart BIT = 1
WHILE @@ROWCOUNT > 0 OR @ForceStart = 1
BEGIN
SET @ForceStart = 0
delete TOP (500) I from
xxx.dbo.charges AS I
where
exists (
select
'to delete'
from
@chargeids c
left join xxx.dbo.chargeitems ci on ci.Charge_Id = c.id
where
ci.id is null AND
c.id = I.Id)
END
如果子查询需要花费很长时间进行处理,则可能要创建一个临时表,该表具有要删除的ID并在循环中与之联接。
请注意,如果要检查xxx.dbo.chargeitems
上的记录是否不存在,则进行NOT EXISTS
的速度要比LEFT JOIN
和IS NULL
快。
编辑:使用临时表保存ID:
-- Create temporary table
IF OBJECT_ID('tempdb..#ChargesToDelete') IS NOT NULL
DROP TABLE #ChargesToDelete
SELECT DISTINCT
c.id
INTO
#ChargesToDelete
from
@chargeids c
left join xxx.dbo.chargeitems ci on ci.Charge_Id = c.id
where
ci.id is null
CREATE CLUSTERED INDEX CI_ChargesToDelete ON #ChargesToDelete (id)
-- Delete rows in batches
SELECT 1 -- Forces @@ROWCOUNT = 1
WHILE @@ROWCOUNT > 0
BEGIN
delete TOP (500) I from
xxx.dbo.charges AS I
where
exists (select 'to delete' from #ChargesToDelete AS C where c.id = I.Id)
END
答案 1 :(得分:0)
您可以尝试以下代码:
declare @deleted table (id int)
declare @amountToDelete int = 2
delete from @deleted
delete top (@amountToDelete) from xxx.dbo.charges
output deleted.* into @deleted
where not exists(select 1 from xxx.dbo.chargeitems
where charge_id = xxx.dbo.charges.id)
while (select count(*) from @deleted) = @amountToDelete
begin
delete from @deleted
delete top (@amountToDelete) from xxx.dbo.charges
output deleted.* into @deleted
where not exists(select 1 from xxx.dbo.chargeitems
where charge_id = xxx.dbo.charges.id)
end
@amountToDelete
表示一次要删除的记录批的大小。类型为table
的另一个变量在循环运行一次时保存已删除的id
。停止循环的条件是删除的记录少于应该删除的记录(这意味着要删除的最后一条记录已经被删除),这与以下条件相对应:
while (select count(*) from @deleted) = @amountToDelete
第一次删除是在循环之前(循环之外)执行的。如果一开始删除所有后退,则代码不会进入循环。