【问题标题】:Large number of UPDATE queries slowing down page大量 UPDATE 查询减慢页面速度
【发布时间】:2011-02-19 16:52:53
【问题描述】:

我正在阅读和验证通过我们的 ASP.net 网站(用 VB.Net 编码)提交的大型固定宽度文本文件(范围从 10-50K 行)。我对文件进行了初始扫描以检查基本问题(行长等)。然后我将每一行导入到一个 MS SQL 表中。每个 DB 行基本上由一个 record_ID(Primary,自动递增)和大约 50 个 varchar 字段组成。

插入完成后,我在文件上运行一个验证函数,根据一系列标准(修剪长度、非数字、范围检查等)检查每一行中的每个字段。如果在任何字段中发现错误,它会在错误表中插入一条记录,该表有一个 error_ID、record_ID 和一条错误消息。此外,如果该字段以特定方式失败,我必须对该字段进行“重置”。重置可能包括将整个字段置空,或者只是将值替换为另一个值(例如,将字符串替换为删除了所有非法字符的新字符串)。

我有一个 5,000 行的测试文件。上传、初始检查和导入大约需要 5-6 秒。详细的错误检查和插入错误表大约需要 5-8 秒(这个文件中大约有 1200 个错误)。但是,对于需要重置的 750 个字段,“重置”部分大约需要 40-45 秒。当我注释掉重置函数(立即返回而不实际调用 UPDATE 存储过程)时,该过程非常快。启用重置后,页面需要 50 秒才能返回。

我的 UPDATE 存储过程正在使用来自 http://sommarskog.se/dynamic_sql.html 的一些推荐代码,因此它使用 CASE 而不是动态 SQL:

UPDATE dbo.Records
SET    dbo.Records.file_ID = CASE @field_name WHEN 'file_ID' THEN @field_value ELSE file_ID END,
.
. (all 50 varchar field CASE statements here)
.
WHERE dbo.Records.record_ID = @record_ID

有什么方法可以帮助我在这里的表现。我可以以某种方式将所有这些 UPDATE 调用分组到一个事务中吗?我应该以某种方式重新处理 UPDATE 查询吗?还是仅仅是 750 多个 UPDATE 的数量,而且速度很慢(它是一个具有 8GB 内存的四进程服务器)。

任何建议表示赞赏。

【问题讨论】:

  • +1 很好地描述了一个相当复杂的过程
  • 您正在查找的列上是否有索引?

标签: sql stored-procedures sql-update


【解决方案1】:

如果您循环遍历这些行并进行单独的更新/插入,这可能会非常昂贵...考虑使用 SqlBulkCopy 可以加快所有插入的速度。同样,您可以创建一个 DataSet,在数据集上进行更新,然后通过 SqlDataAdapter 一次性提交它们。

【讨论】:

  • 我已将插入代码更改为使用 SqlBulkCopy,它运行良好。现在我只需要弄清楚更新。试图将 SqlDataAdapter 选项与 Chris 上面提到的 Table-Valued Parameters 选项进行比较。
【解决方案2】:

嗯。为什么要将数字数据插入 VARCHAR 字段然后尝试对其运行数字检查?这很恶心。

对表应用正确的数据类型和约束,执行 INSERT,看看它是否失败。 SQL Server 很乐意向您报告错误。

【讨论】:

  • 我实际上无法控制数据表布局。我必须使用预先确定的固定宽度文件布局和 DB 表设计。插入不是问题。系统可以非常快速地循环数据、进行验证和插入。只有当我进行第二次验证并且必须进行一些更新时,事情才会变慢。
【解决方案3】:

我相信您每次更新都会做 50 个案例陈述。听起来会很慢。

可以通过参数化查询和字符串常量表注入证明代码来解决这个问题。

快速而肮脏的示例代码。

string [] queryList = { "UPDATE records SET col1 = {val} WHERE ID={key}",
                        "UPDATE records SET col2 = {val} WHERE ID={key}",
                        "UPDATE records SET col3 = {val} WHERE ID={key}",
                         ...
                        "UPDATE records SET col50 = {val} WHERE ID={key}"} 

然后在调用 SQL 时,您只需在数组中选择与要更新的 col 相对应的项目,并为参数化项目设置值和键。

我猜你会看到显着的改进...告诉我进展如何。

【讨论】:

  • 注意:我不动态构建查询的原因是因为这样做可以保证您是防注入的。
  • 我认为 CASE 声明并不是真正的问题。在此之前,我使用的是动态 SQL,它同样慢: SET @sql = 'UPDATE dbo.Records ' + 'SET ' + @field_name + ' = ''' + @field_value + '''' + ' WHERE dbo. Records.record_ID = ' + @record_ID EXEC (@sql)
【解决方案4】:

我会尝试将恢复模式更改为简单并查看我的索引。 Kimberly Tripp 做了一个会议,展示了使用堆提高性能的场景。

【讨论】:

    【解决方案5】:

    不要在 sql 中这样做;用代码修复数据,然后更新。

    如果您有 sql 2008,请查看表值参数。它使您能够将整个表作为参数传递给 s'proc。从他们那里你只有一个插入/更新或合并语句

    【讨论】:

    • 我正在代码中进行所有检查和“修复”。然后我有一个 resetField(record_ID, field_name, field_value) 函数,它调用存储的过程来发送固定数据。但是每个需要修复的字段都会调用一次......我认为这会导致速度变慢。我将研究 Table-Valued Parameters 选项...看起来有可能(我们有 2008 年)。
    • 我最终使用 SqlBulkCopy 进行插入,然后使用 TVP 进行批量更新。速度提升是巨大的。 40-50 秒的处理时间(更新)现在减少到大约 5-7 秒。谢谢大家。
    猜你喜欢
    • 2011-10-04
    • 2015-11-27
    • 2018-05-02
    • 2017-12-24
    • 1970-01-01
    • 2016-08-28
    • 1970-01-01
    • 1970-01-01
    • 2015-09-10
    相关资源
    最近更新 更多