2009-05-13 9 views
5

Ho una query che risiede in più metodi ciascuno (query) di cui può contenere più parametri. Sto cercando di ridurre la dimensione del file e il numero di righe per renderlo più gestibile. Sotto è un tale evento:Come posso rendere più efficienti le query di database multiple in Perl?

$sql_update = qq { UPDATE database.table 
        SET column = 'UPDATE!' 
        WHERE id = ? 
       }; 

$sth_update = $dbh->prepare($sql_update); 

if ($dbh->err) { 
    my $error = "Could not prepare statement. Error: ". $dbh->errstr ." Exiting at line " . __LINE__; 
    print "$error\n"; 
    die; 
} 

$sth_rnupdate->execute($parameter); 

if ($dbh->err) { 
    my $error = "Could not execute statement. Error: ". $dbh->errstr ." Exiting at line " . __LINE__; 
    print "$error\n"; 
    die; 
} 

Questo è solo un esempio, tuttavia, ci sono vari altri esempi selezionati che contengono solo un parametro da passare, ma c'è anche alcuni con due o più parametri. Immagino che mi stia solo chiedendo sarebbe possibile incapsulare tutto questo in una funzione/metodo, passare una serie di parametri, in che modo i parametri saranno popolati nella funzione execute()?

Se ciò fosse possibile, potrei scrivere un metodo che basta semplicemente passare nella query SQL e nei parametri e ottenere un riferimento ai record recuperati. Questo suona sicuro?

risposta

6

Se il numero di righe e il codice gestibile sono l'unico obiettivo, la soluzione migliore sarebbe utilizzare uno qualsiasi dei numerosi framework/librerie ORM disponibili. Class::DBI e DBIx::Class sono due punti di partenza eccellenti. Nel caso in cui, sei preoccupato di dedicare più tempo per imparare questi moduli - non: mi ci è voluto solo un pomeriggio per iniziare e produttivo. Utilizzando Class :: DBI per esempio il vostro esempio è solo una linea:

Table->retrieve(id => $parameter)->column('UPDATE!')->update; 

L'unica nota negativa (se) di questi quadri è che le istruzioni SQL molto complessi necessari a scrivere metodi personalizzati di apprendimento che può richiedere qualche ulteriore tempo (non troppo) per andare in giro.

+0

Questo sembra molto utile. Il modulo Class :: DBI ha perl core? (per unix) –

+0

No. Dovrai installarlo. Digitando "install Class :: DBI" e rispondendo con una "y" a tutte le dipendenze all'interno di una shell CPAN, verrà installato per te. –

2

La funzione "execute" fa accetta un array contenente tutti i parametri.

Non vi resta che trovare un modo per indicare quale dichiarazione gestire che si desidera eseguire e il gioco è fatto ...

Sarebbe molto meglio mantenere la sua dichiarazione gestisce da qualche parte, perché se si crea uno nuovo ogni volta e prepararlo ogni volta che non strappi realmente i vantaggi di "preparare" ...

Informazioni su come restituire tutte le righe che puoi fare (qualcosa come "while fetchrow_hashref push") fai attenzione ai grandi set di risultati che coudl mangia tutta la tua memoria!

+0

Vedo cosa stai dicendo - Dovrò esplorarlo un po '. Tutte le mie dichiarazioni imitano il codice che ho specificato sopra, ma l'unica cosa che è diversa sono i parametri e l'affermazione stessa. –

2

Ecco un approccio semplice utilizzando le chiusure/subs anonimi memorizzati in un hash per parola chiave (compilazione, ma non testati in altro modo), a cura di includere l'uso di RaiseError:

# define cached SQL in hash, to access by keyword 
# 
sub genCachedSQL { 
    my $dbh = shift; 
    my $sqls = shift; # hashref for keyword => sql query 
    my %SQL_CACHE; 
    while (my($name,$sql) = each %$sqls) { 
    my $sth = $dbh->prepare($sql); 
    $SQL_CACHE{$name}->{sth} = $sth; 

    $SQL_CACHE{$name}->{exec} = sub { # closure for execute(s) 
     my @parameters = @_; 
     $SQL_CACHE{$name}->{sth}->execute(@parameters); 

     return sub { # closure for resultset iterator - check for undef 
      my $row; eval { $row = $SQL_CACHE{$name}->{sth}->fetchrow_arrayref(); }; 
      return $row; 
     } # end resultset closure 

    } # end exec closure 

    } # end while each %$sqls 

    return \%SQL_CACHE; 

} # end genCachedSQL 


my $dbh = DBI->connect('dbi:...', { RaiseError => 1 }); 

# initialize cached SQL statements 
# 
my $sqlrun = genCachedSQL($dbh, 
{'insert_table1' => qq{ INSERT INTO database.table1 (id, column) VALUES (?,?) }, 
    'update_table1' => qq{ UPDATE database.table1 SET column = 'UPDATE!' WHERE id = ? }, 
    'select_table1' => qq{ SELECT column FROM database.table1 WHERE id = ? }}); 

# use cached SQL 
# 
my $colid1 = 1; 
$sqlrun->{'insert_table1'}->{exec}->($colid1,"ORIGINAL"); 
$sqlrun->{'update_table1'}->{exec}->($colid1); 
my $result = $sqlrun->{'select_table1'}->{exec}->($colid1); 
print join("\t", @$_),"\n" while(&$result()); 

my $colid2 = 2; 
$sqlrun->{'insert_table1'}->{exec}->($colid2,"ORIGINAL"); 

# ... 
+0

Questo è eccellente. Proverò a implementare questa soluzione un po 'più avanti e ti farò sapere come andrò avanti. –

+1

Contento di contribuire - assicurarsi di testare come si comporta l'eval nei casi di test eseguiti male – bubaker

+0

Domanda veloce Bubaker, Questa query ha eseguito un file fetchall_arrayref() che restituisce TUTTI i record, il che è problematico in quanto la mia query ritorna in eccedenze di 300.000 record con una media di circa 9 colonne. C'è un modo per fare in modo che questa query utilizzi fetchrow_arrayref() o fetchrow_hashref per iterare attraverso i singoli record? –

1

Sono molto impressionato con bubaker di esempio di usare una chiusura per questo.

Proprio lo stesso, se l'obiettivo iniziale era quello di rendere il codice-base più piccola e più gestibile, non posso fare a meno di pensare che c'è un sacco di rumore chiedendo di essere rimosso dal codice originale, prima che qualcuno si imbarca in una conversione a CDBI o DBIC ecc. (nonostante le grandi librerie che entrambi sono.)

Se il $dbh era stato istanziato con RaiseError set negli attributi, la maggior parte di quel codice va via:

$sql_update = qq { UPDATE database.table 
        SET column = 'UPDATE!' 
        WHERE id = ? 
       }; 
$sth_update = $dbh->prepare($sql_update); 
$sth_update->execute($parameter); 

non riesco a vedere che la gestione del codice originale di errore è l'aggiunta di tanto che si non otterrebbe dalla vaniglia die prodotta da RaiseError, ma se è importante, dai un'occhiata all'attributo HandleError nella manpage DBI.

Inoltre, se tali istruzioni non vengono riutilizzate (che è spesso lo scopo principale della loro preparazione, memorizzare nella cache il modo in cui sono ottimizzate, l'altro motivo è di attenuare l'iniezione SQL utilizzando i segnaposto), quindi perché non utilizzare do?

$dbh->do($sql_update, \%attrs, @parameters); 
4

Non ha senso controllare gli errori dopo ogni singola chiamata al database. Che noioso!

Invece, quando ci si collega al database, impostare l'opzione RaiseError su true. Quindi, se si verifica un errore del database, verrà generata un'eccezione. Se non lo prendi (in un blocco eval {}), il tuo programma morirà con un messaggio, simile a quello che stavi facendo manualmente sopra.

Problemi correlati