从 SQL 服务器中的多个表中删除多条记录的最佳做法
Best practices deleting many records from several tables in SQL Server
父 table 有数百万条记录,三个子 table 的外键指向父 table 的主键。像这样:
Parent
parent_id (PK) \ Child1
| child1_id (PK)
|---- parent_id (FK)
|
| Child2
| child2_id (PK)
|---- parent_id (FK)
|
| Child3
| child3_id (PK)
|---- parent_id (FK)
从 Parent
中硬删除数十万条记录的最佳做法是什么?我想在以下条件下删除:DELETE FROM PARENT WHERE [STATUS] = 'DONE'
。有没有办法在删除发生时不锁定 table(s)?以便其他记录可以插入到所有这些 table 中?
我能想到的选项:
- 在外键上使用
CASCADE DELETE
。
- 使用软删除:启动一个事务,
UPDATE parent SET [DELETED] = 1 WHERE [STATUS] = 'DONE'
,删除每个具有这些父 ID 的子项,然后硬删除父项并提交。
- 与 2 类似,但使用过程并将要删除的那些 ID 保存在 table 变量中,这样我就不需要向 [=12] 添加新的
[DELETED]
列=] table.
- Select 要删除的 ID
SELECT parent_id FROM parent WHERE [STATUS] = 'DONE'
然后通过所有这些 ID 进行批量删除。 (这表现得非常糟糕,所以我放弃了它)。
我正在使用 SQL Server 2014 和 spring jdbc。
我更喜欢用TOP x批量删除
所以对于每个 child table :
DELETE TOP 10000
FROM child1
FROM child 1 as c1
INNER join parent
On parent_Id = c1.parent_id
AND parent.[STATUS] = 'DONE'
为每个 child table 重复多个批次。
您可以定期删除 parent 条没有 children 的记录。
DELETE TOP 10000
FROM parent
FROM parent as p
Left outer join child1 c1
On p.parent_Id = c1.parent_id
AND c1.child_id IS NULL
Left outer join child2 c2
On p.parent_Id = c2.parent_id
AND c2.child_id IS NULL
Left outer join child3 c3
On p.parent_Id = c3.parent_id
AND c3.child_id IS NULL
WHERE parent.[STATUS] = 'DONE'
每个 parent 有多少 children 将决定您 运行 和 parent 删除的频率。你当然可以改变 X 我会测试小,然后增加说 50000
Is there a way not to lock the table(s) while the delete is happening?
是的。正如您所建议的,批量操作而不是一次对数百万条记录进行操作将提高并发访问。
我从不使用级联删除,因为它是阴险的:它适用于大量的行,但对数百万行冷淡。而且我从不使用 TOP 任何东西,因为它不合逻辑:它使用任意数字而不是数据的某些方面。
每次我编写这样的程序时,我都会使用相同的技术。从底部开始,一个循环沿着主键删除数据的子集。当删除 returns 0 行受影响时,移动到下一个 table,依此类推,直到您可以删除最上面的行,不留下悬空引用。基本删除看起来像这样:
while @nrows > 0 begin
delete from Child3
where -- limitation criteria -- and
parent_id = (
select min(parent_id)
from Parent
where Status = 'DONE'
)
set @nrows = @@rowcount
done
如果您不能一次删除一个 parent_id
的所有行,出于性能原因,请找到一些限制子集,然后对其进行循环。也许是一个日期,一次删除一个月或一年。如果您可以一次删除多个父项,则一次选择其中的一个子集,并使用 exists
而不是最小值。
幸运的是,为此您不需要用户定义的事务。无论如何,这些行都是敬酒的,您可以在任何地方、任何时候重新开始,无论任何 'DONE' 父级是否仍然存在。
父 table 有数百万条记录,三个子 table 的外键指向父 table 的主键。像这样:
Parent
parent_id (PK) \ Child1
| child1_id (PK)
|---- parent_id (FK)
|
| Child2
| child2_id (PK)
|---- parent_id (FK)
|
| Child3
| child3_id (PK)
|---- parent_id (FK)
从 Parent
中硬删除数十万条记录的最佳做法是什么?我想在以下条件下删除:DELETE FROM PARENT WHERE [STATUS] = 'DONE'
。有没有办法在删除发生时不锁定 table(s)?以便其他记录可以插入到所有这些 table 中?
我能想到的选项:
- 在外键上使用
CASCADE DELETE
。 - 使用软删除:启动一个事务,
UPDATE parent SET [DELETED] = 1 WHERE [STATUS] = 'DONE'
,删除每个具有这些父 ID 的子项,然后硬删除父项并提交。 - 与 2 类似,但使用过程并将要删除的那些 ID 保存在 table 变量中,这样我就不需要向 [=12] 添加新的
[DELETED]
列=] table. - Select 要删除的 ID
SELECT parent_id FROM parent WHERE [STATUS] = 'DONE'
然后通过所有这些 ID 进行批量删除。 (这表现得非常糟糕,所以我放弃了它)。
我正在使用 SQL Server 2014 和 spring jdbc。
我更喜欢用TOP x批量删除
所以对于每个 child table :
DELETE TOP 10000
FROM child1
FROM child 1 as c1
INNER join parent
On parent_Id = c1.parent_id
AND parent.[STATUS] = 'DONE'
为每个 child table 重复多个批次。
您可以定期删除 parent 条没有 children 的记录。
DELETE TOP 10000
FROM parent
FROM parent as p
Left outer join child1 c1
On p.parent_Id = c1.parent_id
AND c1.child_id IS NULL
Left outer join child2 c2
On p.parent_Id = c2.parent_id
AND c2.child_id IS NULL
Left outer join child3 c3
On p.parent_Id = c3.parent_id
AND c3.child_id IS NULL
WHERE parent.[STATUS] = 'DONE'
每个 parent 有多少 children 将决定您 运行 和 parent 删除的频率。你当然可以改变 X 我会测试小,然后增加说 50000
Is there a way not to lock the table(s) while the delete is happening?
是的。正如您所建议的,批量操作而不是一次对数百万条记录进行操作将提高并发访问。
我从不使用级联删除,因为它是阴险的:它适用于大量的行,但对数百万行冷淡。而且我从不使用 TOP 任何东西,因为它不合逻辑:它使用任意数字而不是数据的某些方面。
每次我编写这样的程序时,我都会使用相同的技术。从底部开始,一个循环沿着主键删除数据的子集。当删除 returns 0 行受影响时,移动到下一个 table,依此类推,直到您可以删除最上面的行,不留下悬空引用。基本删除看起来像这样:
while @nrows > 0 begin
delete from Child3
where -- limitation criteria -- and
parent_id = (
select min(parent_id)
from Parent
where Status = 'DONE'
)
set @nrows = @@rowcount
done
如果您不能一次删除一个 parent_id
的所有行,出于性能原因,请找到一些限制子集,然后对其进行循环。也许是一个日期,一次删除一个月或一年。如果您可以一次删除多个父项,则一次选择其中的一个子集,并使用 exists
而不是最小值。
幸运的是,为此您不需要用户定义的事务。无论如何,这些行都是敬酒的,您可以在任何地方、任何时候重新开始,无论任何 'DONE' 父级是否仍然存在。