是否删除包含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中,我可以
删除的行数的阈值是多少?

或者,如果我的方法不是最优的,请建议一个更好的方法。谢谢

这篇文章对我帮助不够:

编辑(2010年8月6日):

好的,在再次阅读上面的链接后,我才意识到这些表上没有索引。另外,你们中的一些人已经在下面的评论中指出了这个问题。请记住,这是一个虚构的模式,所以即使是
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;