我在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;
}
答案 0 :(得分:6)
暂时启用AutoCommit
:
sqlite> .header on sqlite> select * from test; field one two
#!/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; field 1 two
答案 1 :(得分: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 });
}
}
答案 2 :(得分:2)
您的问题是,当您处于抓取循环时,您正在使用相同的数据库处理程序来执行更新。
请使用数据库处理程序的另一个实例来执行更新:
my $dbh = DBI->connect(...);
my $dbhForUpdate = DBI->connect(...) ;
然后在循环中使用dbhForUpdate:
while(my $row = $sth->fetch()){
...
$dbhForUpdate->do(...) ;
}
无论如何,我不建议这样做,因为很有可能在数据库级别遇到并发问题。