Sentencias preparadas y procedimientos almacenados

Muchas de las bases de datos más maduras admiten el concepto de sentencias preparadas. Estas pueden definirse como un tipo de plantillas compiladas para SQL que las aplicaciones quieren ejecutar, pudiendo ser personalizadas utilizando parámetros variables. Las sentencias preparadas ofrecen dos grandes beneficios:

  • La consulta sólo necesita ser analizada (o preparada) una vez, pero puede ser ejecutada muchas veces con los mismos o diferentes parámetros. Cuando la consulta se prepara, la base de datos analizará, compilará y optimizará su plan para ejecutarla. Para consultas complejas, este proceso puede tomar suficiente tiempo como para ralentizar notablemente una aplicación si fuera necesario repetir la misma consulta muchas veces con los mismos parámetros. Mediante el empleo de una sentencia preparada, la aplicación evita repetir el ciclo de análisis/compilación/optimización. Esto significa que las sentencias preparadas utilizan menos recursos y se ejecutan más rápidamente.
  • Los parámetros para las sentencias preparadas no necesitan estar entrecomillados; el controlador automáticamente se encarga de esto. Si una aplicación usa exclusivamente sentencias preparadas, el desarrollador puede estar seguro de que no hay cabida para inyecciones de SQL (sin embargo, si otras partes de la consulta se construyen con datos de entrada sin escapar, aún es posible que ocurran ataques de inyecciones de SQL).

Las sentencias preparadas son tan útiles que son la única característica que PDO emulará para los controladores que no las soporten. Esto asegura que una aplicación sea capaz de emplear el mismo paradigma de acceso a datos independientemente de las capacidades de la base de datos.

Ejemplo #1 Inserciones repetidas utilizando sentencias preparadas

Este ejemplo realiza dos consultas INSERT sustituyendo name y value por los marcadores correspondientes.

<?php
$sentencia
= $mbd->prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)");
$sentencia->bindParam(':name', $nombre);
$sentencia->bindParam(':value', $valor);

// insertar una fila
$nombre = 'uno';
$valor = 1;
$sentencia->execute();

// insertar otra fila con diferentes valores
$nombre = 'dos';
$valor = 2;
$sentencia->execute();
?>

Ejemplo #2 Inserciones repetidas utilizando sentencias preparadas

Este ejemplo realiza dos consultas INSERT sustituyendo name y value por los marcadores posicionales '?'.

<?php
$sentencia
= $mbd->prepare("INSERT INTO REGISTRY (name, value) VALUES (?, ?)");
$sentencia->bindParam(1, $nombre);
$sentencia->bindParam(2, $valor);

// insertar una fila
$nombre = 'uno';
$valor = 1;
$sentencia->execute();

// insertar otra fila con diferentes valores
$nombre = 'dos';
$valor = 2;
$sentencia->execute();
?>

Ejemplo #3 Obtener datos empleando sentencias preparadas

Este ejemplo obtiene datos basándose en un valor de clave proporcionado por un formulario. La entrada del usuario es entrecomillada automáticamente, con lo cual no hay riesgo de un ataque por inyección de SQL.

<?php
$sentencia
= $mbd->prepare("SELECT * FROM REGISTRY where name = ?");
if (
$sentencia->execute(array($_GET['name']))) {
while (
$fila = $sentencia->fetch()) {
print_r($fila);
}
}
?>

Ejemplo #4 Llamar a un procedimiento almacenado con un parámetro de salida

Si el controlador de la base de datos lo admite, una aplicación podría también vincular parámetros para salida y para entrada. Los parámetros de salida se emplean típicamente para recuperar valores de procedimientos almacenados. Los parámetros de salida son ligeramente más complejos de usar que los de entrada, de manera que el desarrollador debe conocer la magnitud de un parámetro dado cuando se vincula. Si el valor resulta ser más grande que el tamaño indicado, se emitirá un error.

<?php
$sentencia
= $mbd->prepare("CALL sp_returns_string(?)");
$sentencia->bindParam(1, $valor_devuleto, PDO::PARAM_STR, 4000);

// llamar al procedimiento almacenado
$sentencia->execute();

print
"El procedimiento devolvió $valor_devuleto\n";
?>

Ejemplo #5 Llamar a un procedimiento almacenado con un parámetro de entrada/salida

Un desarrollador podría también especificar parámetros que contienen valores tanto de entrada como de salida; la sintaxis es similar a la de los parámetros de salida. En el siguiente ejemplo, la cadena 'hola' es pasada al procedimiento almacenado, y cuando éste finaliza, 'hola' es reemplazada con el valor de retorno del procedimiento.

<?php
$sentencia
= $mbd->prepare("CALL sp_takes_string_returns_string(?)");
$valor = 'hola';
$sentencia->bindParam(1, $valor, PDO::PARAM_STR|PDO::PARAM_INPUT_OUTPUT, 4000);

// llamar al procedimiento almacenado
$sentencia->execute();

print
"El procedimiento devolvió $valor\n";
?>

Ejemplo #6 Uso inválido de un marcador de posición

<?php
$sentencia
= $mbd->prepare("SELECT * FROM REGISTRY where name LIKE '%?%'");
$sentencia->execute(array($_GET['name']));

// los marcadores de posición deben emplearse en el lugar del valor completo
$sentencia = $mbd->prepare("SELECT * FROM REGISTRY where name LIKE ?");
$sentencia->execute(array("%$_GET[name]%"));
?>

add a note

User Contributed Notes 3 notes

up
219
adam at pyramidpower dot com dot au
14 years ago
Note that when using name parameters with bindParam, the name itself, cannot contain a dash '-'.

example:
<?php
$stmt
= $dbh->prepare ("INSERT INTO user (firstname, surname) VALUES (:f-name, :s-name)");
$stmt -> bindParam(':f-name', 'John');
$stmt -> bindParam(':s-name', 'Smith');
$stmt -> execute();
?>

The dashes in 'f-name' and 's-name' should be replaced with an underscore or no dash at all.

See http://bugs.php.net/43130

Adam
up
10
w37090 at yandex dot ru
4 years ago
Insert a multidimensional array into the database through a prepared query:
We have an array to write the form:

$dataArr:
Array
(
[0] => Array
(
[0] => 2020
[1] => 23
[2] => 111111
)

[1] => Array
(
[0] => 2020
[1] => 24
[2] => 222222222
)
....

Task: prepare a request and pass through binds
$array = [];
foreach ($dataArr as $k=>$v) {
// $x = 2020, the variable is predetermined in advance, does not change the essence
$array[] = [$x, $k, $v];
}
$sql = ("INSERT INTO `table` (`field`,`field`,`field`) VALUES (?,?,?)");

$db->queryBindInsert($sql,$array);

public function queryBindInsert($sql,$bind) {
$stmt = $this->pdo->prepare($sql);

if(count($bind)) {
foreach($bind as $param => $value) {
$c = 1;
for ($i=0; $i<count($value); $i++) {
$stmt->bindValue($c++, $value[$i]);
}
$stmt->execute();
}
}
}
up
-1
theking2(at)king.ma
10 months ago
Example #5 gives an 1414 wenn tried on MariaDB. Use this function to call a stored procedure with the last parameter as INOUT returning a value like a (uu)id or a count;

<?php
/**
* call_sp Call the specified stored procedure with the given parameters.
* The first parameter is the name of the stored procedure.
* The remaining parameters are the (in) parameters to the stored procedure.
* the last (out) parameter should be an int like state or number of affected rows.
*
* @param mixed $sp_name The name of the stored procedure to call.
* @param mixed $params The parameters to pass to the stored procedure.
* @return int The number of affected rows.
*/
function call_sp( \PDO $db, string $sp_name, ...$params ): mixed
{
$placeholders = array_fill( 0, count( $params ), "?" );
$placeholders[] = "@new_id";

$sql = "CALL $sp_name( " . implode( ", ", $placeholders ) . " ); SELECT @new_id AS `new_id`";

try {
LOG->debug( "calling Stored Procedure", [ "sql" => $sql ] );

$stmt = $db->prepare( $sql );
$i = 0;
foreach(
$params as $param ) {
$stmt->bindValue( ++$i, $param );
}
$stmt->execute();
$new_id = $stmt->fetch( PDO::FETCH_ASSOC )['new_id'];

return
$new_id;

} catch (
\Exception $e ) {
LOG->error( "Error calling Stored Procedure", [ "sql" => $sql, "params" => $params, "error" => $e->getMessage() ] );
throw
$e;
}
To Top