¿Cómo puedo evitar que el programa se cierre cuando el DBI de Perl encuentra un error al preparar una declaración?
Pregunta
Estoy creando un script que pasa por una tabla que contiene todos los otros nombres de tabla en la base de datos. A medida que analiza cada fila, comprueba si la tabla está vacía
select count(*) cnt from $table_name
Algunas tablas ya no existen en el esquema y si lo hago
select count(*)
directamente en el símbolo del sistema, devuelve el error:
206: La tabla especificada (adm_rpt_rec) no está en la base de datos.
Cuando lo ejecuto desde Perl, agrega esto al principio:
DBD :: Informix :: db prepare falló: SQL: -
¿Cómo puedo evitar que el programa se cierre cuando intenta preparar esta declaración SQL?
Solución
Código de trabajo: suponiendo que tenga una base de datos de 'tiendas'.
#!/bin/perl -w
use strict;
use DBI;
my $dbh = DBI->connect('dbi:Informix:stores','','',
{RaiseError=>0,PrintError=>1}) or die;
$dbh->do("create temp table tlist(tname varchar(128) not null) with no log");
$dbh->do("insert into tlist values('systables')");
$dbh->do("insert into tlist values('syzygy')");
my $sth = $dbh->prepare("select tname from tlist");
$sth->execute;
while (my($tabname) = $sth->fetchrow_array)
{
my $sql = "select count(*) cnt from $tabname";
my $st2 = $dbh->prepare($sql);
if ($st2)
{
$st2->execute;
if (my($num) = $st2->fetchrow_array)
{
print "$tabname: $num\n";
}
else
{
print "$tabname: error - missing?\n";
}
}
}
$sth->finish;
$dbh->disconnect;
print "Done - finished under control.\n";
Salida de ejecutar el código anterior.
systables: 72
DBD::Informix::db prepare failed: SQL: -206: The specified table (syzygy) is not in the database.
ISAM: -111: ISAM error: no record found. at xx.pl line 14.
Done - finished under control.
Esto imprimió el error ( PrintError = > 1
), pero continuó. Cambie el 1 a 0 y no aparecerá ningún error. Los paréntesis en las declaraciones de $ tabname
y $ num
son cruciales: contexto de matriz versus contexto escalar.
Otros consejos
Una opción es no usar RaiseError = > 1 al construir $ dbh. El otro es envolver la preparación en un bloque eval.
Simplemente coloque las llamadas que pueden fallar en un bloque de evaluación como este:
for my $table (@tables) {
my $count;
eval {
($count) = $dbi->selectrow_array("select count(*) from $table");
1; #this is here so the block returns true if it succeeds
} or do {
warn $@;
next;
}
print "$table has $count rows\n";
}
Aunque, en este caso, dado que está utilizando Informix, tiene una opción mucho mejor: las tablas del catálogo del sistema. Informix mantiene metadatos como este en un conjunto de tablas de catálogo del sistema. En este caso, quiere sílabas:
my $sth = $dbh->prepare("select nrows from systables where tabname = ?");
for my $table (@tables) {
$sth->execute($table);
my ($count) = $sth->fetchrow_array;
$sth->finish;
unless (defined $count) {
print "$table does not exist\n";
next;
}
print "$table has $count rows\n";
}
Esto es más rápido y seguro que count (*)
en la tabla. La documentación completa de las tablas del catálogo del sistema se puede encontrar en Guía de IBM Informix para SQL (advertencia de que se trata de un PDF).