从CSV文件批量更新/插入数据库


8

我正在实现从一个数据库到另一个数据库的特定于应用程序的数据导入功能。

我有一个包含10000行的CSV文件。这些行需要插入/更新到数据库中。

在某些情况下,数据库中可能存在几行,这意味着需要更新这些行。如果数据库中不存在,则需要将其插入。

一种可能的解决方案是,我可以一行一行地读取,检查数据库中的条目并相应地构建插入/更新查询。但是此过程可能会花费很多时间来创建更新/插入查询并在数据库中执行它们。有时我的CSV文件可能有数百万条记录。

还有其他更快的方法来实现此功能吗?

java  oracle 

尝试分批处理它,否则一击大CSV读数将导致OutOfMemory

@TheNewIdiot如果使用足够的内存(如像服务器那样至少要向JVM提供2 GB内存的像样的服务器)就不会发生。它还将取决于CSV文件中的数据类型以及该进程将在单个进程中运行还是在服务器中的其他进程之后运行。

@Luiggi Mendoza:我同意你的看法。我们有足够的内存来处理生产中的大型CSV文件。

Answers:


7

Oracle中有一项很好的技术,称为外部表。在您的方案中,您可以使用数据库中的“外部表”访问外部纯文本数据,并使用您喜欢并习惯的SQL语句更新数据库中的现有数据,例如INSERTMERGE等等。

在大多数情况下,使用Oracle提供的实用程序是执行ETL的最佳方法。并且由于您的问题听起来更像是管理问题,因此建议您查看我先前在DBA Stack Exchange上发表的文章“从CSV更新Oracle数据库”

更新:这种方法非常适合读取数据库中的外部数据。通常,每次需要处理具有新格式的纯文本文件时,都定义外部数据格式。创建外部表后,您可以像查询真正的数据库表一样对其进行查询。每当有新数据要导入时,您都可以即时替换基础文件,而无需重新创建外部表。由于可以像查询任何其他数据库表一样查询外部表,因此可以编写SQL语句来填充其他数据库表。

与使用手动实现的其他技术相比,使用外部表的开销通常较低,因为该技术在设计时考虑到了Oracle数据库体系结构的性能。


我同意这是实现我的目标的解决方案之一。这种方法如何适合动态CSV处理?就是说,我的应用程序用户有机会上传具有不同格式的多个文件(在这种情况下,需要动态创建外部故事)。同样,一个CSV文件可能包含需要填充到多个表中的数据。

1

我认为您应该使用SQL * Loader将CSV文件加载到临时表中,然后使用MERGE语句将数据插入到工作表中。
与外部表相比,SQL * Loader将为您提供更大的灵活性,如果使用直接路径加载,则它的速度确实非常快。MERGE将完全满足您的需求-插入新记录并更新现有记录。
几个开始的链接:
http : //docs.oracle.com/cd/B19306_01/server.102/b14215/ldr_concepts.htm
http://docs.oracle.com/cd/B28359_01/server.111/b28286/statements_9016 .htm


1
使用SQL Loader将数据加载到数据库中时,DBWR进程或SQL Loader进程会将缓冲区写入数据文件。随后将加载的数据移动到其他表时,数据库将执行另一个I / O。我认为这项额外的工作是没有道理的。顺便说一句,当外部表使用ORACLE_LOADER驱动程序时,用于定义输入数据格式的语法与sqlldr实用程序使用的语法相同,因为它们本质上是相同的技术,因此可以互换使用。在这种情况下,首选外部表,因为不需要先将数据加载到数据库中
Yasir Arsanukaev 2013年

像往常一样,答案是“取决于” :)。在我们的情况下,通常更方便的是先加载到临时表中,然后再处理。由于直接路径负载不会产生重做,因此在其他操作中几乎看不到其他I / O。当然,在其他情况下,其他方法会更好。
Mindaugas Riauba

0

PreparedStatement将使创建插入或更新查询的速度非常快。您应该具有三个PreparedStatements:一个用于插入,一个用于更新,以及一个用于检查表中是否已存在该行。如果您能够使CSV文件和新数据库之间的ID保持相同,则使用primaryID字段检查是否存在一行也应该非常快。

使用批处理插件可以提高性能。在流过CSV文件时,您将检查该行是否已经存在,然后进行更新或将该行添加到批处理插入命令中。您应该检查此SO问题以比较这两种方法的速度。

如果使用我上面概述的方法需要定期进行此数据库导入,而性能是一个问题,那么您可以尝试使用多个工作线程来处理任务。使用与运行此代码的计算机上的处理器一样多的线程。

  int nThreads = Runtime.getRuntime().availableProcessors();

每个线程都有自己的数据库连接,并且随着您的代码遍历整个文件,可以将CSV行传递给各个线程。这要复杂得多,所以只有在性能要求迫使我这样做的情况下,我才这样做。


感谢您的回复。同样,这将需要CSV文件解析并将值填充到准备好的语句中。通过“外部表”方法,我看到文件解析可以移动到数据库端,而应用程序无需关心它。另外,我在应用程序中将JPA与Hibernate一起使用。我正在寻找可以与JPA / Hibernate / Oracle结合使用的选项,它可以简化文件解析,性能好,可维护和灵活的工作。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.