DBD::SQLiteとprepare

ついでに、今回ひっかかっていたところ。

DBD::SQLite 1.12では、こういうスクリプトを実行すると、最後に「closing dbh with active statement handles」という警告が出る。要するに一度も実行されないステートメントハンドルは、finishしてもActiveのまま残ってしまう。

use strict;
use warnings;
use DBI;

unlink 'test.db';

my $db = DBI->connect('dbi:SQLite:test.db');
$db->do('CREATE TABLE test (data TEXT)');

my $sql = 'INSERT INTO test (data) VALUES (?)';
my $st  = $db->prepare($sql);

my @items = ();
$st->execute($_) for @items;

$st->finish;
# undef $st;
$db->disconnect;

1.13の場合は最後の undef $st; を有効にすると警告が消えるんですが、いずれにしてもbuggyなので、いま書いているスクリプトではとりあえず

my $st;
my @items = ();
for (@items) {
  $st = $db->prepare($sql) unless $st;
  $st->execute($_);
}
$st->finish if $st;

という感じに修正。

そういうところに気を遣いたくないときは

my @items = ();
for (@items) {
  $db->do($sql, undef, $_);
}

と書けば警告は出ませんが、もちろん大量にINSERTするときには倍以上遅くなるのを覚悟するべし、と。

あと、上では省略していますが、もちろんSQLiteで大量のデータをINSERTするときはトランザクションを使わないと10倍以上遅くなることもあるというのは有名な話。

ついでに、数万数十万のデータを一度のトランザクションでINSERTしようとするとこれまた激しく重くなるので、適度なところでcommitをはさんだ方がよいというのはPODにも書いてある通り。