是否删除包含4200万行的表的相关子查询的SQL?
我有一个表是否删除包含4200万行的表的相关子查询的SQL?,sql,sql-server,sql-delete,correlated-subquery,Sql,Sql Server,Sql Delete,Correlated Subquery,我有一个表cats,有42795120行 显然,这是一个很大的争吵。因此,当我这样做时: /* owner_cats is a many-to-many join table */ DELETE FROM cats WHERE cats.id_cat IN ( SELECT owner_cats.id_cat FROM owner_cats WHERE owner_cats.id_owner = 1) 查询超时:( (编辑:我需要增加我的命令超时值,默认值仅为30秒) 我不能使用TRUNCAT
cats
,有42795120行
显然,这是一个很大的争吵。因此,当我这样做时:
/* owner_cats is a many-to-many join table */
DELETE FROM cats
WHERE cats.id_cat IN (
SELECT owner_cats.id_cat FROM owner_cats
WHERE owner_cats.id_owner = 1)
查询超时:(
(编辑:我需要增加我的命令超时
值,默认值仅为30秒)
我不能使用TRUNCATE TABLE cat
,因为我不想把其他主人的猫赶走
我使用的SQL Server 2005的“恢复模型”设置为“简单”
因此,我考虑过这样做(顺便说一句,从应用程序执行此SQL):
我的问题是:在SQL Server 2005中,我可以删除的行数的阈值是多少?
或者,如果我的方法不是最优的,请建议一个更好的方法。谢谢
这篇文章对我帮助不够:
id\u cat
也不是PK,因为在我的现实生活模式中,它不是唯一的字段
我将把索引放在:
cats.id\u cat
owner\u cats.id\u cat
owner\u cats.id\u owner
JOIN
字段的索引,对吗
但是,我需要几个小时才能完成这个批量加载过程。我已经在以SqlBulkCopy
的形式(以块为单位,而不是一次完成4200万个)。我有一些索引和PK。我阅读了以下文章,证实了我的理论,即即使是批量复制,索引也会减慢速度:
在复制之前删除我的索引,然后在复制完成后重新创建它们
由于加载时间较长,我需要一段时间来测试这些建议。我将报告结果
更新(2010年8月7日):
汤姆建议:
DELETE
FROM cats c
WHERE EXISTS (SELECT 1
FROM owner_cats o
WHERE o.id_cat = c.id_cat
AND o.id_owner = 1)
仍然没有索引,对于4200万行,用上面描述的方法用了13:21分:秒而不是22:08。然而,对于1300万行,他用了2:13而不是我以前的2:10。这是个好主意,但我仍然需要使用索引
更新(2010年8月8日):
出现了严重的问题!现在打开了索引,我的第一个删除查询花费了1:9小时:min(是的,一小时!)与22:08分:秒和13:21分:秒相比,42百万行和13百万行分别为2:10分:秒。我现在要用索引尝试Tom的查询,但方向不对。请帮助
更新(2010年8月9日):
Tom删除42mil行的时间为1:06小时:分钟,删除13mil行的时间为10:50分钟:秒,而使用索引的时间分别为13:21分钟:秒和2:13分钟:秒。当我使用索引的数量级时,删除数据库的时间会更长!我想我知道为什么,我的数据库.mdf和.ldf在第一季度从3.5 GB增长到40.6 GB(42 mil)删除!我做错了什么?
更新(2010年8月10日):
由于缺乏任何其他选择,我提出了一个我认为是平淡无奇的解决方案(希望是暂时的):
将数据库连接的超时时间增加到1小时(CommandTimeout=60000;
默认值为30秒)
使用Tom的查询:从存在的位置删除(选择1…
,因为它执行得更快一些
在运行delete语句之前删除所有索引和PKs(???)
运行DELETE
语句
创建
所有索引和PK
看起来很疯狂,但至少比使用TRUNCATE
并从第一个owner\u id
开始加载要快,因为我的一个owner\u id
加载需要2:30小时:分钟,而我刚才描述的删除过程需要17:22分钟:秒,行数为42 mil。(注意:如果我的加载过程抛出异常,我将重新开始该owner\u id
,但我不想吹走以前的owner\u id
,因此我不想截断owner\u cats
表,这就是我尝试使用DELETE
的原因。)
我们仍然非常感谢您的帮助:)没有实际的阈值。这取决于您的连接上的命令超时设置
请记住,删除所有这些行所需的时间取决于:
- 查找感兴趣的行所需的时间
- 在事务日志中记录事务所需的时间
- 删除感兴趣的索引项所需的时间
- 删除实际感兴趣行所需的时间
- 等待其他进程停止使用该表所需的时间,以便您可以获取在这种情况下最有可能是独占表锁的内容
最后一点可能是最重要的。在另一个查询窗口中执行sp_who2命令,以确保没有锁争用,从而阻止您的命令执行
配置不当的SQL Server在此类查询中表现不佳。如果事务日志太小和/或与数据文件共享同一磁盘,则在处理大行时,通常会导致严重的性能损失
至于解决方案,就像所有事情一样,这取决于。这是你打算经常做的事情吗?根据你剩下的行数,最快的方法可能是将表重新生成为另一个名称,然后重命名并重新创建它的约束,所有这些都在事务中。如果这只是一个特殊的事情,请确保你的ADO命令是正确的eout设置得足够高,您只需承担这个大删除的成本<
DELETE
FROM cats c
WHERE EXISTS (SELECT 1
FROM owner_cats o
WHERE o.id_cat = c.id_cat
AND o.id_owner = 1)
SELECT 1
WHILE @@ROWCOUNT <> 0
BEGIN
DELETE TOP (somevalue) PERCENT FROM cats
WHERE cats.id_cat IN (
SELECT owner_cats.id_cat FROM owner_cats
WHERE owner_cats.id_owner = 1)
END
Declare @Cats Cursor
Declare @CatId int --assuming an integer PK here
Declare @Start int
Declare @End int
Declare @GroupCount int
Set @GroupCount = 100
Set @Cats = Cursor Fast_Forward For
With CatHerd As
(
Select cats.id_cat
, NTile(@GroupCount) Over ( Order By cats.id_cat ) As Grp
From cats
Join owner_cats
On owner_cats.id_cat = cats.id_cat
Where owner_cats.id_owner = 1
)
Select Grp, Min(id_cat) As MinCat, Max(id_cat) As MaxCat
From CatHerd
Group By Grp
Open @Cats
Fetch Next From @Cats Into @CatId, @Start, @End
While @@Fetch_Status = 0
Begin
Delete cats
Where id_cat Between @Start And @End
Fetch Next From @Cats Into @CatId, @Start, @End
End
Close @Cats
Deallocate @Cats
SELECT *
INTO #cats_to_keep
FROM cats
WHERE cats.id_cat NOT IN ( -- note the NOT
SELECT owner_cats.id_cat FROM owner_cats
WHERE owner_cats.id_owner = 1)
TRUNCATE TABLE cats
INSERT INTO cats
SELECT * FROM #cats_to_keep
DELETE
FROM cats c
WHERE EXISTS (SELECT 1
FROM owner_cats o
WHERE o.id_cat = c.id_cat
AND o.id_owner = 1)
DELETE cats
FROM
cats c
INNER JOIN owner_cats oc
on c.id_cat = oc.id_cat
WHERE
id_owner =1
DELETE cats
FROM
cats c
INNER HASH JOIN owner_cats oc
on c.id_cat = oc.id_cat
WHERE
id_owner =1
DECLARE @RecId bigint
DECLARE @NumRecs bigint
SET @NumRecs = (SELECT MAX(Id) FROM [TableToUpdate])
SET @RecId = 1
WHILE @RecId < @NumRecs
BEGIN
UPDATE [TableToUpdate]
SET UpdatedOn = GETDATE(),
SomeColumn = t2.[ColumnInTable2]
FROM [TableToUpdate] t
INNER JOIN [Table2] t2 ON t2.Name = t.DBAName
AND ISNULL(t.PhoneNumber,'') = t2.PhoneNumber
AND ISNULL(t.FaxNumber, '') = t2.FaxNumber
LEFT JOIN [Address] d ON d.AddressId = t.DbaAddressId
AND ISNULL(d.Address1,'') = t2.DBAAddress1
AND ISNULL(d.[State],'') = t2.DBAState
AND ISNULL(d.PostalCode,'') = t2.DBAPostalCode
WHERE t.Id BETWEEN @RecId AND (@RecId + 49999)
SET @RecId = @RecId + 50000
END
MERGE INTO cats
USING owner_cats
ON cats.id_cat = owner_cats.id_cat
AND owner_cats.id_owner = 1
WHEN MATCHED THEN DELETE;