2009-08-20 40 views
5

我對SQL SELECT語句返回的行有一個循環,並且在對某行的數據進行一些處理之後,我有時想要更新該行的值。循環體中的處理是不平凡的,我不能用SQL編寫它。當我嘗試執行所選行的UPDATE時,出現錯誤(在Perl的DBD :: SQLite :: st下執行失敗:數據庫表被鎖定)。有沒有一種可讀,高效,便攜的方式來實現我想要做的事情?如果失敗了,是否有DBD或SQLite特定的方式來執行此操作?如何更新循環中SELECT所返回的行?

很顯然,我可以把這些更新在單獨的數據結構和循環之後執行它們,但我討厭之後代碼的外觀。

如果你有興趣,這裏是相應的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

太糟糕了你使用perl,Hibernate對於你想要做的事情來說是完美的。 – Zoidberg 2009-08-20 13:03:20

+2

內部它會執行我試圖避免的低效操作。 – 2009-08-20 13:08:52

+6

@Zoidberg,太糟糕了,我們不能downvote無用的評論。 – friedo 2009-08-20 15:29:48

回答

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 
2

更多在回答Zoidberg的評論但如果你能切換到像Perl的DBIx::Class這樣的ORM,那麼你會發現你會發現LD寫是這樣的:

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 ::類不搶你的想象有上CPANFey::ORMRose::DB例如幾個人。

2

你的問題是,你正在使用同一個數據庫處理程序,而你在取環是執行更新。

因此,有你的數據庫處理程序的另一個實例來執行更新:

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

然後在循環使用dbhForUpdate:

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

無論如何,我不建議這樣做,因爲有好您有可能遇到數據庫級別的併發問題。