5

SQL SELECT ステートメントによって返された行にループがあり、行のデータを処理した後、行の値を更新したい場合があります。ループ本体の処理が雑で、SQLで書けない。選択した行の UPDATE を実行しようとすると、エラーが発生します (Perl の DBD::SQLite::st の実行に失敗しました: データベース テーブルがロックされています)。私がやろうとしていることを達成するための読みやすく、効率的で、移植可能な方法はありますか? それができない場合、DBD または SQLite 固有の方法はありますか?

明らかに、更新を別のデータ構造にプッシュしてループ後に実行することはできますが、その後のコードの外観は嫌いです。

興味のある方は、対応する Perl コードを以下に示します。

my $q = $dbh->prepare(q{
  SELECT id, confLoc FROM Confs WHERE confLocId ISNULL});
$q->execute or die;
my $u = $dbh->prepare(q{
  UPDATE Confs SET confLocId = ? WHERE id = ?});
while (my $r = $q->fetchrow_hashref) {
    next unless ($r->{confLoc} =~ m/something-hairy/);
    next unless ($locId = unique_name_state($1, $2));
    $u->execute($locId, $r->{id}) or die;
}
4

3 に答える 3

6

一時的に有効にするAutoCommit:

sqlite> .header on
sqlite> select * from test;
分野
1
2
#!/usr/bin/perl

use strict;
use warnings;

use DBI;

my $dbh = DBI->connect('dbi:SQLite:test.db', undef, undef,
    { RaiseError => 1, AutoCommit => 0}
);

test_select_with_update($dbh);

sub test_select_with_update {
    my ($dbh) = @_;
    local $dbh->{AutoCommit} = 1;
    my $q = $dbh->prepare(q{SELECT field FROM test});
    my $u = $dbh->prepare(q{UPDATE test SET field = ? WHERE field = ?});
    $q->execute or die;
    while ( my $r = $q->fetchrow_hashref ) {
        if ( (my $f = $r->{field}) eq 'one') {
            $u->execute('1', $f) or die;
        }
    }
}

コードが実行された後:

sqlite> .header on
sqlite> select * from test;
分野
1
2
于 2009-08-20T13:27:35.353 に答える
2

Zoidbergのコメントにもっと答えますが、PerlのDBIx :: ClassのようなORMに切り替えることができた場合は、次のようなものを書くことができます。

my $rs = $schema->resultset('Confs')->search({ confLocId => undef });

while ( my $data = $rs->next ) {
    next unless $data->confLoc =~ m/(something)-(hairy)/;
    if ( my $locId = unique_name_state( $1, $2 ) ) {
        $data->update({ confLocID => $locid });
    }
}

また、DBIx :: Classが気に入らない場合は、CPANにFey::ORMやRose::DBなどの他のいくつあります

于 2009-08-20T16:45:28.657 に答える
2

問題は、フェッチ ループ中に同じデータベース ハンドラを使用して更新を実行していることです。

したがって、データベース ハンドラーの別のインスタンスを使用して、更新を実行します。

my $dbh = DBI->connect(...);
my $dbhForUpdate = DBI->connect(...) ;

次に、ループで dbhForUpdate を使用します。

while(my $row = $sth->fetch()){
   ...
   $dbhForUpdate->do(...) ;
}

とにかく、データベース レベルで同時実行性の問題が発生する可能性が高いため、これを行うことはお勧めしません。

于 2009-08-20T19:39:15.997 に答える