2009-05-13 14 views
5

Tengo una consulta que reside en múltiples métodos cada uno (consulta) de los cuales puede contener múltiples parámetros. Estoy tratando de reducir el tamaño del archivo y el recuento de líneas para que sea más fácil de mantener. A continuación se muestra tal ocurrencia:¿Cómo hago que las múltiples consultas de bases de datos sean más eficientes en 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; 
} 

Este es sólo un ejemplo, sin embargo, hay varios otros ejemplos selectos que contienen sólo el parámetro a ser aprobada en, sin embargo también hay algo con dos o más parámetros. Supongo que me pregunto si sería posible encapsular todo esto en una función/método, pasar una matriz de parámetros, ¿cómo se llenarían los parámetros en la función execute()?

Si esto fuera posible, podría escribir un método que simplemente pasa en la consulta SQL y los parámetros y obtener una referencia a los registros recuperados. ¿Esto suena seguro en absoluto?

Respuesta

6

Si el código de línea y el código de mantenimiento es su único objetivo, su mejor opción sería utilizar cualquiera de los varios frameworks/bibliotecas de ORM disponibles. Class::DBI y DBIx::Class son dos buenos puntos de partida. Por si acaso, le preocupa dedicar más tiempo para aprender estos módulos. No: tardé solo una tarde en comenzar y ser productivo. El uso de Class :: DBI por ejemplo, su ejemplo es una sola línea:

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

El único inconveniente (si eso) de estos marcos es que las sentencias SQL muy complicadas requieren el aprendizaje de escribir métodos personalizados que puede llevar algún adicional tiempo (no demasiado) para moverse.

+0

Esto suena muy beneficioso. ¿El módulo Class :: DBI viene con core perl? (para Unix) –

+0

No. Tendrá que instalarlo. Al escribir "install Class :: DBI" y responder con una 'y' a todas las dependencias dentro de un shell CPAN se lo instalará. –

2

La función "ejecutar" hace acepta una matriz que contiene todos sus parámetros.

Sólo hay que encontrar una manera de indicar qué afirmación identificador que desea ejecutar y ya está ...

Sería mucho mejor para mantener su estado de cuenta maneja algún lugar porque si se crea uno nuevo cada vez y prepárelo cada vez que no rompa los beneficios de "preparar" ...

Acerca de devolver todas las filas puede hacer eso (algo así como "mientras fetchrow_hashref push") tenga cuidado con los grandes conjuntos de resultados que ¡cómete toda tu memoria!

+0

Veo lo que dices: tendré que explorar esto un poco. Todas mis declaraciones imitan el código que he especificado anteriormente, pero lo único que es diverso son los parámetros y la declaración en sí misma. –

2

Aquí es un enfoque simple usando cierres/subs anónimos almacenados en un hash por su nombre palabra (compila, pero no prueban lo contrario), editado para incluir el uso de 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

Esto es excelente. Intentaré implementar esta solución un poco más adelante y te contaré cómo me llevo. –

+1

Me alegro de contribuir - definitivamente asegúrese de probar cómo se comporta la evaluación para casos de prueba de ejecución incorrecta – bubaker

+0

Pregunta rápida Bubaker, Esta consulta ejecutó un fetchall_arrayref() que da como resultado recuperar TODOS los registros, lo cual es problemático ya que mi consulta está regresando exceso de 300,000 registros con un promedio de alrededor de 9 columnas. ¿Hay alguna manera de hacer que esta consulta utilice fetchrow_arrayref() o fetchrow_hashref para recorrer los registros individuales? –

1

Estoy muy impresionado con bubaker de ejemplo de usar un cierre para esto.

De la misma manera, si el objetivo original era hacer la base de código más pequeña y más fácil de mantener, no puedo evitar pensar que hay mucho ruido pidiendo ser eliminado del código original, antes de que alguien se embarque en una conversión a CDBI o DBIC, etc. (a pesar de las grandes bibliotecas que ambos son.)

Si el $dbh habían creado una instancia con RaiseError conjunto de los atributos, la mayor parte de ese código desaparece:

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

no puedo ver que la manipulación en el código original de error es la adición de tanto que se no se obtendría del vainilla die producido por RaiseError, pero si es importante, eche un vistazo al atributo HandleError en la página de manual DBI.

Además, si tales declaraciones no se están reutilizando (que a menudo es el propósito principal de prepararlas, almacenar en caché cómo se optimizan, la otra razón es mitigar la inyección SQL mediante el uso de marcadores), ¿por qué no? use do?

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

No hay sentido en la comprobación de errores después de cada llamada a la base de datos. ¡Qué tedioso!

En su lugar, cuando se conecta a la base de datos, establezca la opción RaiseError en verdadero. Luego, si ocurre un error en la base de datos, se lanzará una excepción. Si no lo detecta (en un bloque eval {}), su programa morirá con un mensaje, similar a lo que estaba haciendo manualmente arriba.

Cuestiones relacionadas