2016-07-23 93 views
2

拥有包含数百万条记录的父表和具有指向父表的主键的三个子表的外键。像这样:从SQL Server中的多个表中删除多条记录的最佳做法

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'。有没有办法在删除发生时锁定表格?那么其他记录可以插入所有这些表中? 我可以想到的选项:

  1. 使用CASCADE DELETE上的外键。
  2. 使用软删除:开始一个事务,UPDATE parent SET [DELETED] = 1 WHERE [STATUS] = 'DONE',删除每个孩子与父母的ID,然后硬删除父母并提交。
  3. 类似于2,但使用过程并将这些ID保存在表变量中,以便我不需要在Parent表中添加新的[DELETED]列。
  4. 选择要删除的ID SELECT parent_id FROM parent WHERE [STATUS] = 'DONE'然后做一个批量删除传递所有这些ID。 (这真的很糟糕,所以我放弃了它)。

我正在使用SQL Server 2014和spring jdbc。

+1

我建议在外键上使用级联删除,每次删除多达50,000条记录的批处理。删除大量记录通常会在将其分解为小批量时加速。 –

+1

我是测量操作进度的粉丝,所以我会考虑编写一个程序来删除小部分的记录。或者使用ON CASCADE DELETE或自己做。 –

回答

1

我更喜欢使用前X

所以对于每个子表批量删除:对于每个子表

DELETE TOP 10000 
FROM child1 
FROM child 1 as c1 
INNER join parent 
On parent_Id = c1.parent_id 
AND parent.[STATUS] = 'DONE' 

重复多批次。

定期地,您可以删除没有孩子的父记录。

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' 

每个家长有多少个孩子将决定您运行父删除的频率。你当然可以改变X我会测试小,然后增加说50000

+0

因为可以插入新的'DONE'记录,所以我不能仅仅执行'删除顶部10000 ...父母'[STATUS] ='DONE'',我不想删除这些记录。我可能会保存所有记录的ID在表变量或临时表上删除,然后按照您的建议批量删除。 – otonakav

+1

如何通过附加子句来声明限制日期或通过id命令来确保只删除较旧的记录 – Mike

+0

听起来很好,谢谢 – otonakav

1

有没有办法在删除发生时锁定表(s)?

是的。正如你所建议的那样,一次批量操作而不是数百万条记录会改善并发访问。

我从来没有使用级联删除,因为它是阴险的:它可以很好地处理行的操作,但可以防止数百万人受到冷遇。我从不使用TOP,因为它不合逻辑:它可以使用任意数字而不是数据的某个方面。

每次我写这样的程序时,我都使用了相同的技巧。从底部开始,循环会删除主键上的数据子集。当删除返回0行受影响,移动到下一个表,等等,直到你可以删除最上面一行,没有任何悬挂引用。基本缺失看起来是这样的:

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而不是最低限度。

幸运的是,为此目的,您不需要用户定义的事务。无论如何,这些行都是烤面包,您可以随时随地重新启动,无论是否存在“完成”父项。