2009-08-20 5 views
5

Ho un ciclo sulle righe restituite da un'istruzione SQL SELECT e, dopo l'elaborazione sui dati di una riga, a volte desidero AGGIORNARE il valore della riga. L'elaborazione nel corpo del ciclo non è banale e non riesco a scriverlo in SQL. Quando provo ad eseguire l'UPDATE per la riga selezionata ottengo un errore (in DBD di Perl :: SQLite :: st execute failed: la tabella del database è bloccata). Esiste un modo leggibile, efficiente e portabile per ottenere ciò che sto cercando di fare? In caso contrario, esiste un modo DBD o SQLite specifico per farlo?Come posso AGGIORNARE le righe restituite da una SELECT in un ciclo?

Ovviamente, posso inviare gli aggiornamenti in una struttura dati separata ed eseguirli dopo il ciclo, ma dopo di ciò odierò l'aspetto del codice.

Se sei interessato, ecco il codice Perl corrispondente.

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

Peccato che usi perl, Hibernate sarebbe perfetto per quello che vuoi fare. – Zoidberg

+2

Un interno eseguirà l'operazione inefficiente che sto cercando di evitare. –

+6

@Zoidberg, peccato che non si possano minimizzare i commenti inutili. – friedo

risposta

6

temporaneamente abilitare 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; 
     } 
    } 
} 

Dopo che il codice è stato eseguito:

 
sqlite> .header on 
sqlite> select * from test; 
field 
1 
two 
2

Più in risposta alle commento di Zoidberg ma se il vostro sono stati in grado di cambiare ad un ORM come il numero DBIx::Class di Perl, allora trovi che tu sia qualcosa di ld scrittura come questo:

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 }); 
    } 
} 

E se DBIx :: classe non afferrare la vostra fantasia vi sono un paio di altri su come CPANFey::ORM e Rose::DB per esempio.

2

Il problema è che si sta utilizzando lo stesso gestore di database per eseguire un aggiornamento mentre si è in un ciclo di recupero.

Quindi avere un'altra istanza del gestore di database per eseguire gli aggiornamenti:

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

Quindi utilizzare dbhForUpdate in loop:

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

In ogni caso, io non consiglierei di fare questo dal momento che c'è una buona è probabile che si verifichino problemi di concorrenza a livello di database.