2013-03-11 3 views
3

나는 "SQL 방식"으로 서로를 비교하여 텍스트 파일을 많이 처리합니다.DBI :: CSV 구현 Sqlite를 기반으로

DBD::CSV은 텍스트 테이블에 SQL 구문을 사용할 수 있으므로 분명히 시작하는 것이 좋습니다. 그러나 거대한 텍스트 파일을 다루므로 성능면에서 쓸데없는 DBD::CSV을 만들었습니다.

그래서, 나는 csv 파일을 sqlite DB로 변환하는 모듈을 작성한 다음 재생할 수있는 DBI::sqlite 객체를 반환합니다. 문제는, 텍스트 파일을 sqlite 테이블로 변환하는 것이 매우 효율적이지 않다는 것입니다. 왜냐하면 펄로드에서 sqlite 명령 줄을 실행하여 CSV 파일을 빠르게로드 할 수 없기 때문입니다 (.load를 사용). 그래서 텍스트 테이블을 기반으로 커다란 Insert into 문자열을 만들고 실행해야합니다 (라인 단위로 라인 삽입은 성능 측면에서 매우 비효율적이므로 하나의 큰 삽입을 실행하는 것이 좋습니다). 나는 그것을 피하기 위해 기꺼이 csv를 sqlite에 perl을 사용하여로드하기위한 한 줄짜리 탐색기를 찾는다.

그리고 또 다른 한가지

, 내가 실행하기 위해 다음과 같은 기능을 사용하고 인쇄 멋지게 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 파일)을 실행하는 데 걸리는 내가 기대할 수있는 길은 길다. 누구든지보다 효율적인 솔루션을 가지고 있습니까?

감사합니다.

답변

5

Text::CSV_XS은 매우 빠르며 성능 문제의 측면에서 CSV를 처리해야합니다.

DBD :: SQLite performant를 만들기 위해 특별한 대량 삽입 코드가 필요하지 않습니다. 바인드 매개 변수가있는 insert 문은 매우 빠릅니다. 주요 트릭은 DBI에서 AutoCommit을 끄고 모든 트랜잭션을 단일 트랜잭션으로 수행하는 것입니다.

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; 

내 맥북에서 1.5 초 만에 5.7M CSV 파일을 통과했습니다. 이 파일은

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

조금 더 빨리 바인드 열을 사용하여 만들 수있을 ... 70,000 선으로 채워졌다, 그러나 나의이 테스트에서 물건을 둔화.

+0

나는 단지 $ conn-> begin_work과 $ conn-> commit으로 같은 줄을 생각하고 있었다. SQLite는 커다란 인서트가있는 슬러그입니다. – charlesbridge

+0

고마워요! 좋은 형식으로 쿼리를 효율적으로 인쇄하는 방법은 무엇입니까? – Mattan

+0

@Mattan "인쇄 쿼리"란 무엇을 의미합니까? 새로 생성 된 데이터베이스에 대한 쿼리를 의미합니까? 다른 질문 같아. – Schwern

관련 문제