SQL Server / MySQL / Access - 以低效的方式加速插入许多行

Posted

技术标签:

【中文标题】SQL Server / MySQL / Access - 以低效的方式加速插入许多行【英文标题】:SQL Server / MySQL / Access - speeding up inserting many rows in an inefficient manner 【发布时间】:2009-01-07 15:22:36 【问题描述】:

设置

我必须在 SQL Server 2000/2005、mysql 或 Access 中插入几百万行。不幸的是,我没有一种简单的方法来使用批量插入或 BCP 或任何其他普通人会做的方式。插入将发生在一个特定的数据库上,但该代码需要与 db 无关——所以我不能进行批量复制、SELECT INTO 或 BCP。但是,我可以在插入之前和之后运行特定查询,具体取决于我要导入的数据库。

例如。

If IsSqlServer() Then
    DisableTransactionLogging();
ElseIf IsMySQL() Then
    DisableMySQLIndices();
End If

... do inserts ...

If IsSqlServer() Then
    EnableTransactionLogging();
ElseIf IsMySQL() Then
    EnableMySQLIndices();
End If

问题

我可以对 SQL Server 做些什么有趣的事情来加速这些插入?

例如,我可以发出一个命令来告诉 SQL Server,“嘿,不要费心在事务日志中记录这些事务”。

或者我可以说,“嘿,我有一百万行进来了,所以在我完全完成之前不要更新你的索引”。

ALTER INDEX [IX_TableIndex] ON Table DISABLE
     ... inserts
ALTER INDEX [IX_TableIndex] ON Table REBUILD

(注意:以上索引禁用仅适用于 2005 年,不适用于 2000 年。如果您知道在 2000 年执行此操作的方法,则可以加分)。

MySQL 和 Access 怎么样?

【问题讨论】:

您使用的是 API 或命令行工具还是什么?为什么或以什么方式“没有一种简单的方法来使用批量插入或 BCP 或任何其他普通人会做的方式”? 事实证明我的 INSERTS 必须与 db 无关,因此它必须与 MySQL、SQL Server 2000 和 Access 一起使用。但是我可以在插入之前和之后进行一些数据库特定的调整以加快速度。所以我不能完全重写插入来做批量插入。 如果它必须与数据库无关,那么也许您应该更新标题和标签以更准确地了解您的具体情况。 当您说“必须使用 ... Access”时,您的意思是您在 Access 中编程,或者您想将数据附加到 Jet 数据库(这是 Access 的默认数据存储)?当然,这是有区别的,因为 Access 是开发环境,而 Jet 是数据库引擎。 【参考方案1】:

在这里会影响性能的最重要的事情是(听起来像是)您正在对数据库执行一百万个不同的 INSERT。每个 INSERT 都被视为单个操作。如果您可以将其作为单个操作执行,那么您几乎肯定会获得巨大的性能提升。

MySQL 和 SQL Server 都支持“选择”不带表名的常量表达式,因此这应该作为一个语句工作:

INSERT INTO MyTable(ID, name)
SELECT 1, 'Fred'
UNION ALL SELECT 2, 'Wilma'
UNION ALL SELECT 3, 'Barney'
UNION ALL SELECT 4, 'Betty'

我不清楚 Access 是否支持这一点,没有 Access 可用。但是,据我所知,Access 确实支持 SELECT 中的常量,并且您可以将上述内容强制转换为 ANSI SQL-92(所有 3 个引擎都应该支持;它与您的“数据库不可知论”一样接近)会得到)只需添加

FROM OneRowTable

到每个单独的 SELECT 的末尾,其中“OneRowTable”是一个只有一行虚拟数据的表。

这应该让您在远少于一百万条 INSERT 语句中插入一百万行数据——并且诸如索引重新洗牌之类的事情将只完成一次,而不是一百万次。在那之后,您可能对其他优化的需求要少得多。

【讨论】:

【参考方案2】:

这是一个常规过程还是一次性事件?

过去,我只是编写当前索引的脚本,删除它们,插入行,然后重新添加索引。

SQL Management Studio 可以从右键菜单中编写索引...

【讨论】:

【参考方案3】:

对于 SQL Server:

    您可以将恢复模式设置为“简单”,这样您的事务日志将保持较小。之后别忘了退后一步。

    禁用索引实际上是个好主意。这将适用于 SQL 2005,而不是 SQL Server 2000。

    禁用 [TABLE_NAME] 上的更改索引 [INDEX_NAME]

并启用

alter index [INDEX_NAME] on [TABLE_NAME] rebuild

然后一一插入行。你必须要有耐心,但至少要快一些。

【讨论】:

除非您正在执行日志传送,在这种情况下您可能不想将恢复模式设置为简单,除非您想丢失一些数据。【参考方案4】:

如果这是一次性的事情(或者它经常发生足以证明自动化的合理性),还要考虑删除/禁用所有索引,然后在插入完成时再次添加/重新启用它们

【讨论】:

他的问题中不是提到他不能使用批量插入吗? 在我写这个答案的时候,他的问题中没有提到。他在看到这个后补充说。【参考方案5】:

将恢复模式设置为简单的问题在于它会影响同时输入数据的任何其他用户,因此会使他们的更改无法恢复。

与禁用索引相同,这对每个人都禁用,并且可能使数据库运行速度比 slug 慢。

建议您批量运行导入。

【讨论】:

我实际上控制着这个,所以这个时候没有其他人会使用数据库。【参考方案6】:

如果这不是需要快速阅读的内容,您可以在 MySQL 上的表中执行"Insert Delayed"。这允许您的代码继续运行,而无需等待插入实际发生。这确实有一些限制,但如果您主要关心的是让程序快速完成,这可能会有所帮助。请注意,有很多情况可能无法按预期运行。检查the docs。

不过,我不知道此功能是否适用于 Access 或 MS SQL。

【讨论】:

已被弃用,在 5.7 中不再工作。【参考方案7】:

您是否考虑过使用工厂模式?我猜你正在为此编写代码,所以如果使用工厂模式,你可以编写一个返回具体“IDataInserter”类型类的工厂,该类可以完成这项工作。

这仍然允许您与数据无关,并为每种类型的数据库获得最快的方法。

【讨论】:

【参考方案8】:

SQL Server 2000/2005、MySQL 和 Access 都可以直接从 tab / cr 文本文件加载,它们只是有不同的命令来完成。如果您有 case 语句来确定您要导入哪个数据库,只需弄清楚他们对导入文本文件的偏好。

【讨论】:

在 SQL 中使用 SSIS 或 DTS 将使文本文件加载过程标准化,并将目标系统中的变量排除在外。【参考方案9】:

您可以使用 DTS (2000) 或 SSIS (2005) 构建一个包来执行此操作吗? DTS 和 SSIS 都可以从同一个源中提取并通过管道输出到不同的潜在目的地。如果可以,请选择 SSIS。里面有很多好的、快速的技术以及嵌入 IsSQLServer、IsMySQL 等逻辑的功能。

【讨论】:

【参考方案10】:

值得考虑breaking your inserts into smaller batches;包含大量查询的单个事务会很慢。

【讨论】:

【参考方案11】:

您可以考虑在批量插入期间使用 SQL 的批量日志恢复模型。

http://msdn.microsoft.com/en-us/library/ms190422(SQL.90).aspx

http://msdn.microsoft.com/en-us/library/ms190203(SQL.90).aspx

您还可以在插入期间禁用目标表上的索引。

【讨论】:

以上是关于SQL Server / MySQL / Access - 以低效的方式加速插入许多行的主要内容,如果未能解决你的问题,请参考以下文章

访问不允许SQL-Server列中的空值的INSERT或UPDATE(访问运行时错误3162)

sql server 2008和mysql的区别

mysql和sql server有啥区别

sql server 2008和mysql的区别

安装过了sql server之后,又安装mysql但是mysql不出现配置

SQL Server connect to MySQL SQL Server通过LinkServer访问MySQL数据库