首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何将数据写入文件以加载到MySQL数据库?

问如何将数据写入文件以加载到MySQL数据库?
EN

Stack Overflow用户
提问于 2014-01-06 17:20:17
回答 2查看 1.1K关注 0票数 0

我遇到了一些I/O瓶颈,将解析的数据插入到MySQL数据库中。因此,我不需要解析文件并将每个记录分别插入数据库,而是将记录写入文件(如CSV),然后手动将文件加载到数据库中。

我如何使用Perl来处理这个问题?下面是当前将数据插入数据库的代码,但我希望以一种格式将数据写入文件,以便以后尽可能地将数据插入到MySQL中。

代码语言:javascript
复制
#INSERT INTO ANNOUNCE TABLE
foreach my $au (@ANNOUNCED) {

    my $val=$au->[0];
    my $IP = $prefix->ip();
    my $subnetmask = $prefix->mask();


    $Announce_update->execute($IP,$subnetmask,$UpdateKey);

}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2014-01-06 19:19:31

要从外部文件加载数据,MySQL提供了LOAD DATA INFILE命令。LOAD DATA INFILE在输入文件格式时相当灵活,允许您指定分隔符、EOL字符、字段是否被引用等。它与案文:CSV协同工作,可以用来输出包含数据的分隔文件。

首先,将数据写入文件:

代码语言:javascript
复制
use Text::CSV;

my $csv = Text::CSV->new({ eol => "\n" }) or die Text::CSV->error_diag();

my $infile = "/path/to/file";
open my $fh, ">", $infile or die $!;

for my $i (0..$#ANNOUNCED)
{
   # Don't end last line with '\n' or we'll get a garbage row when we load
   # to the database
   $csv->eol(undef) if $i == $#ANNOUNCED;

   # Generate the data to insert for this row

   # Write to file
   $csv->print($fh, [ $IP, $subnetmask, $UpdateKey ]);
}

# Close file handle to flush the buffer
close $fh;

请注意,您必须在写入文件句柄后关闭该文件句柄,否则MySQL可能无法获取所有数据。

接下来,加载文件:

代码语言:javascript
复制
my $query = "LOAD DATA LOCAL INFILE '$infile' INTO TABLE table
             FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\"'
             LINES TERMINATED BY '\\n'";

eval {
   # $dbh is an already open database handle
   my $rows_inserted = $dbh->do($query);
};
die $@ if $@;

LOCAL关键字影响文件是否位于MySQL服务器上。如果要加载到本地主机上的数据库,则可以关闭LOCAL关键字,这听起来可能有违直觉(使用LOCAL,客户端将文件的副本发送到服务器的临时目录,服务器从那里读取文件,因此它是服务器本地的)。如果没有LOCAL关键字,该文件必须对所有人都具有可读性(即至少0644对*nix的权限,因为您还必须对它进行写入)。MySQL还将根据是否使用LOCAL在不同的位置查找相对路径;有关详细信息,请参阅文档。

上面的查询假设您的表只有三列。如果有更多内容,则需要指定要为哪些列插入数据,按照CSV中出现的顺序排列,例如:

代码语言:javascript
复制
my $query = "LOAD DATA LOCAL INFILE '$infile' INTO TABLE table
             FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '\"'
             LINES TERMINATED BY '\\n'
             (ip, subnetmask, updatekey)";

复合刀片

如果您一次插入大量行,LOAD DATA INFILE是目前最快的方法。然而,对于较小的插入,写入和读取临时文件的额外I/O开销(特别是如果使用LOCAL选项)比使用这种复合插入要慢:

代码语言:javascript
复制
# Inserts 3 rows at once
INSERT INTO table VALUES (foo, foo, foo), (bar, bar, bar), (baz, baz, baz)

对于许多行,LOAD DATA INFILE比复合插入的速度更快。对于我的数据库和应用程序,我做了一些分析,发现这个数字大约是100,但对您来说几乎肯定是不同的。我编写了一个函数,根据要插入的数据行数选择最有效的方法:

代码语言:javascript
复制
sub insert_rows {
    my $data = shift; # Reference to an AoA

    my $num_rows = $#{ $data };

    if ($num_rows < 100) {
        # Generate compound insert statement
    }
    else {
        # LOAD DATA INFILE
    }
}

请注意,max_allowed_packet的默认值在MySQL 5.7中仅为1MB。如果复合insert语句超过此值,您将得到一个Packet too large错误,插入将失败。您可以将其调整到最大1GB,但到那时,您可能已经达到了LOAD DATA INFILE效率更高的临界值。

票数 1
EN

Stack Overflow用户

发布于 2014-01-06 18:23:11

在这一点上,使用案文:CSV可能会使您的生活更轻松。如果您的系统尚未安装该模块,请安装该模块,然后将这一行添加到您的脚本中:

代码语言:javascript
复制
use Text::CSV ;

注释掉代码中的数据库插入位:

代码语言:javascript
复制
 # $dbh->prepare("SET FOREIGN_KEY_CHECKS = 0;")->execute();
 # $Announce_update->execute($IP,$subnetmask,$UpdateKey);

然后尝试获取您想要的数据:

代码语言:javascript
复制
 my @csvdata = ($IP,$subnetmask,$UpdateKey); # assuming data is in these vars 
                                              # and they aren't references used 
                                              # by $Announce_update somehow

然后将其写入某个地方的CSV文件(从文档中摘录),您将“手动”加载这些文件(我假设您是指使用MySQL控制台或CLI工具)。

代码语言:javascript
复制
  my $csv = Text::CSV->new ( { binary => 1 } )  
  open $fh, ">:encoding(utf8)", "csvdata.csv" or die ;
  $csv->print ($fh, $_) for @csvdata;

希望这能帮上忙--尽管手头还有点短。即使成功了,我也不确定你这样做是否会让你的生活更轻松;-)

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20955522

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档