4

私はテキストファイルをよく扱い、「SQL方式」で相互に比較します。

DBD::CSVテキストテーブルでSQL構文の力を使用できるので、最初は明らかに良い選択です。しかし、私は巨大なテキストファイルを扱っているので、DBD::CSVパフォーマンスの面では役に立たない。

DBI::sqliteそれで、私はcsvファイルをsqlite DBに変換し、それから私が遊ぶことができるオブジェクトを返すモジュールを書き始めていました。重要なのは、テキストファイルをsqliteテーブルに変換することもあまり効率的ではない可能性があるということです。perlからsqliteコマンドラインを実行してCSVファイルをすばやくロードすることができないためです(.loadを使用)。したがってInsert into、テキストテーブルに基づいて巨大な文字列を作成し、それを実行する必要があります(Insertを1行ずつ実行すると、パフォーマンスの点で非常に非効率的であるため、1つの大きな挿入を実行することをお勧めします)。私はそれを避けたいと思っています。perlを使用してcsvをsqliteにロードするためのワンライナーを探しています。

また、次の関数を使用して、SQLクエリを適切に実行および出力しました。

sub sql_command {
my ($self,$str) = @_;
my $s = $self->{_db}->prepare($str) or die $!;
$s->execute() or die $!;
my $table;
push @$table, [ map { defined $_ ? $_ : "undef" } @{$s->{'NAME'}}];
while(my $row = $s->fetch) {
    push @$table, [ map{ defined $_ ? $_ : "undef" }@$row ];
}
box_format($table);
return box_format($table);;
}


sub box_format {
my $table = shift;
my $n_cols = scalar @{$table->[0]};

my $tb = Text::Table->new(\'| ', '', (\' | ','')x($n_cols-1), \' |+');
$tb->load(@$table);
my $rule = $tb->rule(qw/- +/);
my @rows = $tb->body();
return $rule, shift @rows, $rule, @rows, $rule
    if @rows;
}

サブのsql_command実行には約1分かかります(6.5 MBファイルの場合)。これは、私の意見では、予想よりもはるかに長いです。誰かがより効率的な解決策を持っていますか?

ありがとう!

4

1 に答える 1

6

Text :: CSV_XSは非常に高速であり、CSVを処理するためにそれを使用すると、パフォーマンスの問題のその側面を処理する必要があります。

DBD :: SQLiteのパフォーマンスを向上させるために、特別な一括挿入コードは必要ありません。バインドパラメータを使用した挿入ステートメントは非常に高速です。主なトリックは、DBIでAutoCommitをオフにし、1つのトランザクションですべての挿入を実行することです。

use v5.10;
use strict;
use warnings;
use autodie;

use Text::CSV_XS;
use DBI;

my $dbh = DBI->connect(
    "dbi:SQLite:dbname=csvtest.sqlite", "", "",
    {
        RaiseError => 1, AutoCommit => 0
    }
);

$dbh->do("DROP TABLE IF EXISTS test");

$dbh->do(<<'SQL');
CREATE TABLE test (
    name        VARCHAR,
    num1        INT,
    num2        INT,
    thing       VARCHAR,
    num3        INT,
    stuff       VARCHAR
)
SQL

# Using bind parameters avoids having to recompile the statement every time
my $sth = $dbh->prepare(<<'SQL');
INSERT INTO test
       (name, num1, num2, thing, num3, stuff)
VALUES (?,    ?,    ?,    ?,     ?,    ?    )
SQL

my $csv = Text::CSV_XS->new or die;
open my $fh, "<", "test.csv";
while(my $row = $csv->getline($fh)) {
    $sth->execute(@$row);
}
$csv->eof;
close $fh;

$sth->finish;    
$dbh->commit;

これは私のMacbookで1.5秒で570万のCSVファイルを実行しました。ファイルは70,000行でいっぱいでした...

"foo",23,42,"waelkadjflkajdlfj aldkfjal dfjl",99,"wakljdlakfjl adfkjlakdjflakjdlfkj"

バインド列を使用して少し速くすることは可能かもしれませんが、私のテストでは速度が低下しました。

于 2013-03-11T11:24:34.997 に答える