SELECT en Perl
Recuperar datos de MySQL es la operación más frecuente en cualquier script Perl que trabaje con bases de datos. DBI ofrece múltiples métodos para ejecutar consultas y procesar resultados, desde la recuperación fila por fila hasta atajos que simplifican las consultas más comunes. En este artículo aprenderás las diferentes técnicas disponibles y cuándo usar cada una.
Requisitos previos
Necesitas una conexión DBI configurada y la tabla productos con datos de prueba:
USE tienda;
INSERT INTO productos (nombre, categoria, precio, stock) VALUES
('Laptop HP Pavilion', 'Computadoras', 12999.99, 25),
('Mouse Logitech MX Master', 'Periféricos', 1599.00, 150),
('Teclado Mecánico Corsair K70', 'Periféricos', 2299.50, 80),
('Monitor Samsung 27"', 'Monitores', 6499.00, 40),
('Auriculares Sony WH-1000XM5', 'Audio', 5999.99, 60),
('Webcam Logitech C920', 'Periféricos', 1299.00, 200);Código completo
Este ejemplo ejecuta una consulta parametrizada y muestra los resultados:
#!/usr/bin/perl
use strict;
use warnings;
use DBI;
my $dbh = DBI->connect(
"DBI:mysql:database=tienda;host=localhost",
"root", "tu_contraseña",
{ RaiseError => 1, AutoCommit => 1, mysql_enable_utf8mb4 => 1 }
);
my $categoria = 'Periféricos';
my $sth = $dbh->prepare(
"SELECT id, nombre, precio, stock FROM productos WHERE categoria = ? ORDER BY precio DESC"
);
$sth->execute($categoria);
print "Productos en categoría \"$categoria\":\n";
while (my $row = $sth->fetchrow_hashref()) {
printf " [%d] %s - \$%.2f (%d en stock)\n",
$row->{id}, $row->{nombre}, $row->{precio}, $row->{stock};
}
print "Total: ", $sth->rows(), " productos\n";
$sth->finish();
$dbh->disconnect();Salida esperada:
Productos en categoría "Periféricos":
[3] Teclado Mecánico Corsair K70 - $2299.50 (80 en stock)
[2] Mouse Logitech MX Master - $1599.00 (150 en stock)
[6] Webcam Logitech C920 - $1299.00 (200 en stock)
Total: 3 productos
Explicación paso a paso
El proceso de consulta en DBI sigue el patrón prepare-execute-fetch. Después de ejecutar la consulta, usas uno de los métodos fetch para recuperar las filas. El método fetchrow_hashref() devuelve cada fila como una referencia a un hash donde las claves son los nombres de las columnas.
Métodos de fetch
DBI ofrece varios métodos para recuperar resultados, cada uno con un formato diferente:
my $sth = $dbh->prepare("SELECT nombre, precio FROM productos WHERE id = ?");
$sth->execute(1);
# fetchrow_hashref - Hash con nombres de columna (más legible)
my $hash = $sth->fetchrow_hashref();
print "Nombre: $hash->{nombre}, Precio: $hash->{precio}\n";
# fetchrow_arrayref - Referencia a array (más rápido)
$sth->execute(1);
my $ref = $sth->fetchrow_arrayref();
print "Nombre: $ref->[0], Precio: $ref->[1]\n";
# fetchrow_array - Array (más directo)
$sth->execute(1);
my ($nombre, $precio) = $sth->fetchrow_array();
print "Nombre: $nombre, Precio: $precio\n";Obtener todas las filas
# fetchall_arrayref - Todas las filas como array de arrays
my $sth = $dbh->prepare("SELECT nombre, precio FROM productos WHERE activo = 1");
$sth->execute();
my $filas = $sth->fetchall_arrayref();
for my $fila (@$filas) {
print "$fila->[0]: \$$fila->[1]\n";
}
# fetchall_arrayref con hash - Todas las filas como array de hashes
$sth->execute();
my $datos = $sth->fetchall_arrayref({});
for my $producto (@$datos) {
print "$producto->{nombre}: \$$producto->{precio}\n";
}
# fetchall_hashref - Hash indexado por una columna clave
$sth = $dbh->prepare("SELECT id, nombre, precio FROM productos");
$sth->execute();
my $por_id = $sth->fetchall_hashref('id');
# Acceder directamente por ID
print "Producto 1: $por_id->{1}->{nombre}\n";Atajos de DBI
DBI ofrece métodos de conveniencia en el handle de la base de datos que combinan prepare, execute y fetch en una sola llamada:
# selectall_arrayref - Todas las filas en un solo paso
my $productos = $dbh->selectall_arrayref(
"SELECT nombre, precio FROM productos WHERE categoria = ?",
{ Slice => {} }, # Devolver como hashes
'Periféricos'
);
for my $p (@$productos) {
print "$p->{nombre}: \$$p->{precio}\n";
}
# selectrow_hashref - Una sola fila como hash
my $producto = $dbh->selectrow_hashref(
"SELECT nombre, precio, stock FROM productos WHERE id = ?",
undef, 1
);
if ($producto) {
print "Producto: $producto->{nombre} (\$$producto->{precio})\n";
} else {
print "Producto no encontrado\n";
}
# selectrow_array - Una sola fila como array
my ($nombre, $precio) = $dbh->selectrow_array(
"SELECT nombre, precio FROM productos WHERE id = ?",
undef, 1
);
print "Nombre: $nombre, Precio: $precio\n";
# selectcol_arrayref - Una sola columna como array
my $nombres = $dbh->selectcol_arrayref(
"SELECT nombre FROM productos WHERE activo = 1 ORDER BY nombre"
);
print "Productos: ", join(", ", @$nombres), "\n";Caso práctico
Veamos una función de búsqueda con paginación:
#!/usr/bin/perl
use strict;
use warnings;
use DBI;
my $dbh = DBI->connect(
"DBI:mysql:database=tienda;host=localhost",
"root", "tu_contraseña",
{ RaiseError => 1, AutoCommit => 1, mysql_enable_utf8mb4 => 1 }
);
sub buscar_productos {
my (%args) = @_;
my $pagina = $args{pagina} || 1;
my $por_pagina = $args{por_pagina} || 10;
my $sql = "SELECT id, nombre, categoria, precio, stock FROM productos WHERE activo = 1";
my @params;
if ($args{busqueda}) {
$sql .= " AND nombre LIKE ?";
push @params, "%$args{busqueda}%";
}
if ($args{categoria}) {
$sql .= " AND categoria = ?";
push @params, $args{categoria};
}
if (defined $args{precio_min}) {
$sql .= " AND precio >= ?";
push @params, $args{precio_min};
}
if (defined $args{precio_max}) {
$sql .= " AND precio <= ?";
push @params, $args{precio_max};
}
# Contar total
(my $sql_count = $sql) =~ s/SELECT .+ FROM/SELECT COUNT(*) FROM/;
my ($total) = $dbh->selectrow_array($sql_count, undef, @params);
# Paginación
my $offset = ($pagina - 1) * $por_pagina;
$sql .= " ORDER BY nombre LIMIT ? OFFSET ?";
push @params, $por_pagina, $offset;
my $datos = $dbh->selectall_arrayref($sql, { Slice => {} }, @params);
return {
datos => $datos,
total => $total,
pagina => $pagina,
total_paginas => int(($total + $por_pagina - 1) / $por_pagina)
};
}
my $resultado = buscar_productos(
categoria => 'Periféricos',
precio_min => 1000,
precio_max => 3000,
pagina => 1,
por_pagina => 5
);
print "Mostrando ", scalar(@{$resultado->{datos}}),
" de $resultado->{total} resultados\n";
print "Página $resultado->{pagina} de $resultado->{total_paginas}\n";
for my $p (@{$resultado->{datos}}) {
print " $p->{nombre} ($p->{categoria}) - \$$p->{precio}\n";
}
$dbh->disconnect();Salida esperada:
Mostrando 3 de 3 resultados
Página 1 de 1
Mouse Logitech MX Master (Periféricos) - $1599.00
Teclado Mecánico Corsair K70 (Periféricos) - $2299.50
Webcam Logitech C920 (Periféricos) - $1299.00
Manejo de errores
sub consulta_segura {
my ($dbh, $sql, @params) = @_;
my $datos;
eval {
$datos = $dbh->selectall_arrayref($sql, { Slice => {} }, @params);
};
if ($@) {
my $error = $@;
if ($error =~ /doesn't exist/) {
print "La tabla especificada no existe\n";
} elsif ($error =~ /Unknown column/) {
print "Una de las columnas especificadas no existe\n";
} elsif ($error =~ /You have an error in your SQL syntax/) {
print "Error de sintaxis en la consulta SQL\n";
} else {
print "Error en la consulta: $error\n";
}
return [];
}
return $datos;
}Ahora que dominas las consultas SELECT en Perl, en el siguiente artículo aprenderás a eliminar datos de MySQL.
Escrito por Eduardo Lázaro
