【问题标题】:How do I insert 800000 records into an MS Access table?如何在 MS Access 表中插入 800000 条记录?
【发布时间】:2009-02-06 20:24:18
【问题描述】:

我需要在 MS Access 表中插入 800000 条记录。我正在使用 Delphi 2007 和 TAdoXxxx 组件。该表包含一些整数字段、一个浮点字段和一个只有一个字符的文本字段。其中一个整数字段(不是 autoinc)有一个主键,另一个整数和浮点字段有两个索引。

使用AdoTable.AppendRecord(...) 插入数据需要> 10 分钟,这是不可接受的,因为每次用户开始在程序中使用新数据库时都会这样做。我无法预填充表格,因为数据来自另一个数据库(无法通过ADO 访问)。

通过将记录写入制表符分隔的文本文件并使用tAdoCommand 对象执行,我设法缩短到大约 1 分钟

insert into table (...) select * from [filename.txt] in "c:\somedir" "Text;HDR=Yes"

但我不喜欢这样的开销。

我认为一定有更好的方法。

编辑:

一些附加信息:

  • 之所以选择 MS Access,是因为它不需要在目标计算机上进行任何额外安装,并且整个数据库都包含在一个文件中,可以轻松复制。
  • 这是一个单用户应用程序。
  • 数据将只插入一次,并且在数据库的生命周期内不会更改。但是,该表包含一个附加字段,用作标志,以指示另一个数据库中的相应记录已被用户处理。
  • 一分钟可以接受的(最多 3 分钟也可以),我的解决方案有效,但对我来说似乎太复杂了,所以我认为应该有更简单的方法来做到这一点。
  • 插入数据后,表的性能相当不错。
  • 当我开始规划/实施与 Access 数据库一起使用的程序的功能时,不需要该表。直到后来,当客户要求另一个功能时,它才变得有必要。 (不总是这样吗?)

编辑:

从到目前为止我得到的所有答案来看,我似乎已经找到了将这么多数据插入 Access 表的最快方法。感谢大家,感谢您的帮助。

【问题讨论】:

  • 您需要多快?考虑到它花了 10 分钟,现在只需要 1
  • 1 分钟插入 800000 条记录似乎是可以接受的。
  • 1 分钟是完全可以接受的,但我想离开而不创建这个文本文件。
  • 创建文本文件有什么问题?
  • 感觉不对。我已经在内存中获取了数据,但为了将其插入数据库,我必须先将其存储到文件中。

标签: delphi ms-access ado delphi-2007


【解决方案1】:

既然你说 800K 记录数据在数据库的生命周期内不会改变,我建议将文本文件作为表格链接,并完全跳过插入。

如果你坚持拉入数据库,那么1分钟800000条记录超过13000条/秒。我认为您不会在 MS Access 中击败它。

如果您希望它对用户响应更快,那么您可能需要考虑加载一些最小的数据集,并设置一个后台线程以在它们工作时加载其余部分。

【讨论】:

  • 我需要数据库中的数据,因为用户可能会将其从一台计算机复制到另一台计算机以继续处理它。链接将要求他也复制文本文件并保留其路径。目标用户的计算机知识不足以完成此操作。
  • 简单地将文本文件作为表格导入需要多长时间(即不要将其插入任何地方,直接导入即可?)
【解决方案2】:

没有索引会更快。可以在导入后添加吗?

此线程Slow MSAccess disk writing 中可能有一些您可能感兴趣的建议

【讨论】:

  • 是的,我想过这个,但一些试验并没有真正显示出太大的性能提升。
【解决方案3】:

跳过文本文件并使用 ODBC 或 OLEDB 直接从源表导入会怎样?这将意味着更改您的 FROM 子句以使用源表名称和适当的连接字符串作为 FROM 子句的 IN '' 部分。

编辑: 实际上我看到你说原始格式是 xBase,所以应该可以使用作为 Jet 一部分的 xBase ISAM 而不是需要 ODBC 或 OLEDB。看起来像这样:

INSERT INTO table (...) 
SELECT * 
FROM tablename IN 'c:\somedir\'[dBase 5.0;HDR=NO;IMEX=2;];

您可能需要对此进行调整——我刚刚抓取了指向 DBF 文件的链接表的连接字符串,因此参数可能略有不同。

【讨论】:

  • 这可能只是可能的,但它需要对模块进行一些相当丑陋的重新设计(目前不知道源表,但只有一个用于获取数据的接口)。我明天再检查一下。
  • 我对您的代码知之甚少,无法知道为什么会出现问题——因为您在文本文件上执行 INSERT,看来您只是跳过创建文本文件并用表中的直接 INSERT 替换它,如果您在运行时才知道表名,则使用动态编写的 SQL。
【解决方案4】:

您的基于文本的解决方案似乎是最快的,但如果您可以获得接近末尾大小的预分配 MS Access,则可以更快地获得它。您可以通过填充典型的用户数据库、关闭应用程序(以便刷新缓冲区)并手动删除该大表的所有记录来做到这一点 - 但不缩小/压缩它。

因此,使用该文件开始真正的填充 - Access 不会请求任何(或很少)额外的磁盘空间。如果 MS Access 有办法自动执行此操作,请不要记住,但它可以提供很大帮助...

【讨论】:

  • 有趣的想法。不幸的是,它对我不起作用,因为我不知道该表实际上会变成多大(800000 条记录是我使用的测试用例,实际大小取决于访问数据库时不可用的不同表的内容创建。)也许我会做一些测试,看看是否将它填充到例如500000 条记录大大加快了后期导入的速度,足以让它值得一试。
  • 我的经验是针对 SQLServer,但其他数据库引擎(例如:Firebird)不会出于完全相同的原因自动收缩数据库文件:尽可能避免向底层操作系统询问新的磁盘空间分配。甚至 SQLServer 中的还原操作也会预先分配磁盘空间。
  • 你进步了多少,dummzeuch?
【解决方案5】:

换个方式怎么样...

是否可以选择复制具有您需要的此表的现有 Access 数据库文件,然后删除除此大表之外的所有其他数据(不知道 Access 是否具有等效于SQL server 中的“截断表”之类的东西)?

【讨论】:

  • 如果没有,那么至少“从表中删除 *”应该可以工作
【解决方案6】:

我会用另一个数据库替换 MS Access,对于您的情况,我认为 Sqlite 是最好的选择,它不需要在客户端机器上进行任何安装,它是非常快速的数据库,也是最好的嵌入式数据库解决方案之一.

您可以通过两种方式在 Delphi 中使用它:

  1. 您可以从Sqlite网站下载数据库引擎Dll,并使用免费Delphi组件访问它,如Delphi SQLite componentsSQLite4Delphi

  2. 使用内置引擎的DISQLite3,您不必在应用程序中分发dll,他们有免费版本;-)

如果你仍然需要使用MS Access,尝试直接使用带有SQL Insert 语句的TAdoCommand,而不是使用TADOTable,应该比使用TADOTable.Append 更快;

【讨论】:

  • 不幸的是,此时在项目中替换 Access 不是一个选项
  • 另外,您确定它会使导入记录更快吗?
【解决方案7】:

正如有人提到的,您不会在一分钟内导入 800,000 条记录;这真的已经很快了。

但是,如果您使用正确的方法(DAO 记录集)进行插入,您可以跳过烦人的翻译到文本文件的步骤。请参阅我在 StackOverflow 上提出并回答的上一个问题:MS Access: Why is ADODB.Recordset.BatchUpdate so much slower than Application.ImportXML?

即使使用 DAO,也不要使用 INSERT INTO;它很慢。也不要使用 ADO;它很慢。但是 DAO + Delphi + Recordsets + 直接实例化 DbEngine COM 对象(而不是通过 Access.Application 对象)会给你很大的速度。

【讨论】:

  • 有趣的方法。稍后我会试一试(有问题的程序已经附带了文本文件导入方法)。
【解决方案8】:

您正在以一种方式寻找正确的方向。使用单个语句进行批量插入比尝试遍历数据并逐行插入要快。 Access 作为基于文件的数据库,在迭代写入中会非常慢。

问题在于 Access 正在处理它如何在内部优化写入,实际上并没有任何方法可以控制它。您可能已经达到了 INSERT 语句的最大效率。为了提高速度,您可能应该评估是否有任何方法可以在每次启动应用程序时将 800,000 条记录写入数据库。

【讨论】:

  • OP 说它只发生在用户创建新数据库时,而不是每次应用程序启动时。那将是一个问题:)
  • 不是每次启动应用程序。只有当我开始使用新的访问数据库时才会发生这种情况,该数据库是使用不同的工具创建的。添加数据后,在数据库的整个生命周期内都不会更改。
【解决方案9】:

获取SQL Server Express(免费)并从访问外部表连接到它。 SQL express 比 MS Access 快得多。

【讨论】:

  • 在这种情况下,这不是一个选项。
  • 嵌入 SQL Server - 无需安装,只需一个 DLL。仍然比 Access 好很多。
【解决方案10】:

我会预先填充数据库,然后将文件本身交给他们,而不是填充现有的(但为空的)数据库。

如果你要填写的数据发生变化,那么在服务器上保持一个ODBC访问数据库(MDB文件)同步,使用一些代码来查看主数据库中的变化并将它们复制到访问数据库中。

当用户请求新数据库时,压缩 MDB,将其传输给他们,然后打开它。

或者,您可以找到直接打开数据并将数据插入数据库的代码。

或者,您也许可以找到另一种可以更快地访问导入的格式(csv 除外)。

-亚当

【讨论】:

  • 嗯,你的最后一句话让我想到:目前另一个数据库是dbase,我认为ADO也可以读取dbase,所以我实际上可以直接在插入语句中使用那个原始表......
  • 听起来越来越像原始数据根本没有改变。如果是这样,那么我认为您最好的选择是将数据写入访问数据库文件一次,然后在用户需要“新”数据库时复制文件
  • 不,这不是一个选项。访问数据库将根据自定义配置创建。唯一会一直存在于该数据库中的是有问题的表。另外,有多个主数据库,我需要从用户开始使用访问数据库时处于活动状态的数据库中复制。
  • 我们在谈论什么样的自定义配置?复制数据库将跳过创建数据库的步骤,您只需复制现有文件。复制后是否还可以进行任何自定义配置?
【解决方案11】:

还要检查复制文件需要多长时间。这将是您写入数据的速度的下限。在 db 之类的 SQL 中,通常需要批量加载实用程序才能接近该速度。据我所知,MS 从未像 bcp 那样创建直接写入 MS Access 表的工具。专门的 ETL 工具也会优化一些围绕插入的步骤,比如 SSIS 在内存中进行转换的方式,DTS 也有一些优化。

【讨论】:

    【解决方案12】:

    也许您可以使用锁定模式adLockBatchOptimistic 和 CursorLocation adUseClient 打开表的 ADO 记录集,将所有数据写入记录集,然后进行批量更新 (rs.UpdateBatch)。

    【讨论】:

      【解决方案13】:

      如果是来自dbase,你可以直接复制数据和索引文件并直接附加而不加载吗?应该非常高效(来自为您带来 FoxPro 的人。)我想它也会使用现有的索引。

      至少,它应该是一个相当高效的单命令导入。

      【讨论】:

      • 不,我不能。新表实际上包含一个额外的列,我在以后的操作中填充和查询。
      【解决方案14】:

      这 800,000 条记录从一次创作到另一次创作改变了多少?是否可以预先填充记录,然后在创建新数据库时仅更新外部数据库中已更改的记录?

      这可以让您更快地创建新的数据库文件。

      【讨论】:

      • 所有这些记录都可能发生变化,因为用户有可能(甚至很可能)切换到不同的主数据库。
      【解决方案15】:

      您的磁盘转动速度有多快?如果是 7200RPM,那么 3 分钟内 800,000 行仍然是磁盘每转 37 行。我不认为你会做得比这更好。

      同时,如果目标是简化流程,那么表格链接怎么样?

      您说您无法通过 ADO 访问源数据库。您可以在 MS Access 中设置表链接到源数据库中的表或视图吗?然后,来自表链接的简单追加查询将为您将数据从源数据库复制到目标数据库。我不确定,但我认为这会很快。

      如果您在运行时之前无法设置表链接,也许您可​​以通过 ADO 以编程方式构建表链接,然后以编程方式构建追加查询,然后调用追加查询。

      【讨论】:

      • access 数据库和 master 数据库都可能因用户操作而更改,因此在 access 数据库中创建链接的唯一方法是在运行时。我现在将坚持使用文本文件解决方案,因为它足够快且易于实施,而且我不知道如何通过 ADO 创建链接。
      【解决方案16】:

      你好 正如他们所说,最好的方法是从 txt 文件中批量插入 您应该将记录插入到 txt 文件中,然后将 txt 文件批量插入到表中 该时间应该小于 3 秒。

      【讨论】:

        猜你喜欢
        • 2012-11-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-06-08
        • 1970-01-01
        相关资源
        最近更新 更多