【问题标题】:SQL SERVER Delete very large number of rows optimizationSQL SERVER 删除非常大的行数优化
【发布时间】:2015-05-22 16:30:33
【问题描述】:

假设我有一个有 5 列的表:

Id int primary key

Date datetime 

Value double

Fund_id reference

FundModel_id reference

FundDataField_id reference

此表有 37 000 000 行。

每个基金大约有 4000 行。从该表中删除行的最佳且快速的方法是什么? 我需要一次删除大约 7000000 行,但这大约需要 10 分钟,对我来说这已经很多了。

目前我按 Fund_Id 删除行,如下所示:

Delete from FundYearDetail where Fund_id In (2054,2056,2058,2059,2061,2063,2064,2065,2066,2067,2069,2072,2073,2076,2078,2079,2080,2081,2082,
2086,2088,2090,2093,2095,2096,2097,2099,2101,2102,2103,2104,2105,2106,2107,2109,2110,2114,2115,2116,2117,2118,2119,2342,2125,2126,2127,2128,2129,2130,2131)

这条语句将影响大约 200 000 行并且需要很长时间才能完成,将这条语句分成 2 个查询我得到了更好的性能,每个查询大约 4 秒。

有人知道更好的解决方案吗?

注意:如果有人知道使用 Nhibernate 的更好解决方案,我正在使用 Fluent NHibernate 进行数据访问,请告诉我。 如果我要做一个存储过程,这会提高我的性能吗? 谢谢。

【问题讨论】:

  • 这是一次性操作,还是您需要添加到应用程序中以使您的软件的最终用户可重复并完成的一项功能? (无论如何,我可能不会使用 NH,假设您正在为所有这些实体补水只是为了删除它们)
  • 好吧,我在正常情况下使用 NH 访问数据,我想删除所有这些行只是因为我正在从 csv 文件执行大数据导入,我正在使用 sqlbulkcopy 执行此操作,所以在导入数据之前我想要删除所有将受到影响的行而不是更新它们,因为我认为更新这么多行将花费更多时间而不是使用 sqlbulkcopy 插入它们。
  • 是的,我正在寻找一种可重复的解决方案,但如果您也有一次性解决方案,请告诉我您有哪一个,我认为您不会建议重新创建表格...
  • 与大更新一样,我会尝试使用 while 循环,一次删除 50,000 条或更少的记录。
  • @TabAlleman 所说的是在 sql 中进行大型删除的最佳方法 - 这里已经回答了一些不同的问题,它们使用循环、goto、top 子句来完成这项工作。我在使用 NH 进行任何批量操作时一次又一次地遇到问题——最好的办法是将整个操作推送到数据库中——存储过程集、SSIS 等——然后你可以更有效地进行更新,也许避免删除。尤其是如果你有外键,删除的效果会非常好。

标签: c# sql sql-server stored-procedures nhibernate


【解决方案1】:

您可以像这样进行批量删除:

SELECT 'Starting' --sets @@ROWCOUNT
WHILE @@ROWCOUNT <> 0
    DELETE TOP (50000) dbo.timesheet  --change top value as needed
    WHERE Fund_id IN (2054,2056,2058,2059,2061,2063,2064,2065,2066,2067,2069,2072,2073,2076,
                      2078,2079,2080,2081,2082,2086,2088,2090,2093,2095,2096,2097,2099,2101,
                      2102,2103,2104,2105,2106,2107,2109,2110,2114,2115,2116,2117,2118,2119,
                      2342,2125,2126,2127,2128,2129,2130,2131           
            )

感谢@gbn:Bulk Delete on SQL Server 2008

更新

或者,您可以尝试这种方法,方法是在临时表中插入要保留的记录,然后截断您的实际表。然后,将这些临时表记录传输回您的实际表中。不确定您会从中获得多少性能增益,但绝对建议您在执行此操作之前执行BACKUP

SELECT col1, col2, col3, col4, col5 INTO #Holdingtable
       FROM FundYearDetail WHERE Fund_id NOT IN (2054,2056,2058,2059,2061,2063,2064,2065,
                    2066,2067,2069,2072,2073,2076,2078,2079,2080,2081,2082,2086,2088,2090,
                    2093,2095,2096,2097,2099,2101,2102,2103,2104,2105,2106,2107,2109,2110,
                    2114,2115,2116,2117,2118,2119,2342,2125,2126,2127,2128,2129,2130,2131           
            )
TRUNCATE TABLE FundYearDetail

INSERT FundYearDetail (
    col1
    ,col2
    ,col3
    ,col4
    ,col5
    )
SELECT 
     col1
    ,col2
    ,col3
    ,col4
    ,col5
FROM #Holdingtable

【讨论】:

  • SQL SERVER EXPRESS 上的 200000 行在 43 秒内被删除。不太好。
  • 这并不意味着解决方案无效,性能问题可能是由于您的数据库日志文件增长、阻塞、回滚等造成的。
  • @Greg,好吧,我可以在 10 秒内完成,我不是在寻找有效的解决方案,我很抱歉,但有效的解决方案可以是简单的删除,我正在寻找最佳解决方案。
  • @MDDDC 很遗憾听到我之前的回答对性能没有太大帮助。我已经用另一种方法(有点冒险)更新了我的答案,但一定要试一试。
  • @MDDDC,我的意思是,因为您遇到性能不佳并不意味着它不是最好的解决方案。还有其他因素可能导致业绩不佳。日志文件增长就是其中之一,您可以通过适当调整日志文件的大小来解决它。事务复制使用批量删除来清理元数据,它一次删除 5K 行,同样的方式,在亚秒内。它可以非常快速地删除数百万行。
【解决方案2】:

这样怎么样:

decalre @tableIds table (Id int)

insert into @tableIds
select 2054 as Id union all
...
//here is other ids
...
select 2131 as Id

while exists(select 1 from FundYearDetail t1 join @table t2 on t1.Fund_id = t2.Id)
begin
delete top(10000) t1
from FundYearDetail t1 join @table t2 on t1.Fund_id = t2.Id
end

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-18
    • 1970-01-01
    • 2022-10-29
    • 2017-07-22
    相关资源
    最近更新 更多