Guía rápida
Esta guía rápida le ayudará a elegir y obtener familiaridad
con la API MySQL de PHP.
Esta guía rápida ofrece información general sobre la extensión mysqli. Se proporcionan
ejemplos de código para los aspectos más importantes de la API. Los conceptos de la base de datos se explican
con el grado necesario para presentar conceptos específicos de MySQL.
Se requiere: Familiaridad con el lenguaje de programación PHP, con el lenguaje SQL,
y conociemientos básicos del servidor MySQL.
Interfaz dual: procedimental y orientada a objetos
La extensión mysqli ofrece una interfaz dual. Soporta el paradigma de programación
procedimental y el orientado a objetos.
Los usuarios que migren desde la extensión mysql antigua pueden preferir la interfaz
procedimental. Esta interfaz es similar a la de la extensión antigua de
mysql. En la mayoría de los casos, los nombres de funciones difieren únicamente por el prefijo.
Algunas funciones de mysqli toman como primer argumento un gestor de conexión,
mientras que las funciones similares de la antigua interfaz de mysql lo
toman como el último argumento opcional.
Migración sencilla desde la antigua extensión mysql
]]>
&example.outputs;
La interfaz orientada a objetos
Además de la clásica interfaz procedimental, los usuarios pueden optar por usar
la interfaz orientada a objetos. La documentación está organizada según
la interfaz orientada a objetos. Esta interfaz muestra las funciones
agrupadas por su propósito, haciendo más fácil los comienzos. La sección de referencia
proporciona ejemplos para ambas variantes de sintaxis.
No existen diferencias significativas de rendimiento entre las dos interfaces.
Los usuarios puede basar su elección en sus preferencias personales.
Interfaz orientada a objetos y procedimental
connect_errno) {
echo "Fallo al conectar a MySQL: " . $mysqli->connect_error;
}
$resultado = $mysqli->query("SELECT 'elecciones para complacer a todos.' AS _msg FROM DUAL");
$fila = $resultado->fetch_assoc();
echo $fila['_msg'];
?>
]]>
&example.outputs;
Se usa la interfaz orientada a objetos en el inicio rápido porque la
sección de referencia está organizada de esta manera.
Mezclar estilos
Es posible cambiar entre los estilos en cualquier momento. No se recomienda mezclar
los dos estilos por razones de claridad y estilo de código.
Estilo de codificación malo
connect_errno) {
echo "Fallo al conectar a MySQL: " . $mysqli->connect_error;
}
$resultado = mysqli_query($mysqli, "SELECT 'Estilo malo pero posible.' AS _msg FROM DUAL");
if (!$resultado) {
echo "Fallo al ejecutar la consulta: (" . $mysqli->errno . ") " . $mysqli->error;
}
if ($fila = $resultado->fetch_assoc()) {
echo $fila['_msg'];
}
?>
]]>
&example.outputs;
Véase tambiénmysqli::__constructmysqli::querymysqli_result::fetch_assoc$mysqli::connect_errno$mysqli::connect_error$mysqli::errno$mysqli::errorEl resumen de funciones de la extensión MySQLiConexiones
El servidor MySQL soporta el uso de diferentes capas de
transporte para conexiones. Las conexiones usan TCP/IP, sockets de dominio Unix o
tuberías con nombre de Windows.
El nombre del host localhost tiene un significado especial.
Está vinculado al uso de sockets de dominio Unix. No es posible
abrir una conexión TCP/IP usando como nombre de host localhost,
se debe usar 127.0.0.1 en su lugar.
Significado especial de localhost
connect_errno) {
echo "Fallo al conectar a MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error;
}
echo $mysqli->host_info . "\n";
$mysqli = new mysqli("127.0.0.1", "usuario", "contraseña", "basedatos", 3306);
if ($mysqli->connect_errno) {
echo "Fallo al conectar a MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error;
}
echo $mysqli->host_info . "\n";
?>
]]>
&example.outputs;
Valores predeterminados de los parámetros de conexión
Dependiendo de la función de conexión usada se pueden omitir
varios parámetros. Si no se proporciona un parámetro, la extensión intentará
usar los valores predeterminados que están establecidos en el fichero de configuración de PHP.
Configuración predeterminada
Los valores resultantes de los parámetros son pasados a la biblioteca cliente
que esté usando esta extensión. Si la biblioteca cliente detecta parámetros vacíos o no
establecidos, puede usar los valores internos predeterminados de la biblioteca.
Valores predeterminados internos de conexión de la biblioteca
Si el valor del host no está establecido o está vacío, la biblioteca cliente
usará una conexión de socket Unix sobre localhost.
Si el socket no está establecido o está vacío, y es solicitada una conexión de socket Unix,
se intentará una conexiónal socket predeterminado de
/tmp/mysql.sock.
En sistemas Windows, el nombre de host . es interpretado
por la biblioteca cliente como un intento de abrir una conexión basada en una tubería con nombre
de Windows. En este caso el parámetro del socket se interpreta como el nombre de la
tubería. Si no se proporciona o está vacío, se usará como socket (nombre de la tubería)
\\.\pipe\MySQL.
Si no está establecida una conexión basada en un socket de dominio Unix ni en una
tubería con nombre de Windows y el valor del parámetro del puerto no está establecido, la biblioteca
usuará como puerto predeterminado el 3306.
La biblioteca mysqlnd y la
Biblioteca Cliente de MySQL (libmysqlclient) implementan la misma lógica para determinados valores predeterminados.
Opciones de conexión
Las opciones de conexión están disponibles para, por ejemplo, establecer
comando iniciales que son ejecutados sobre la conexión, o para solicitar el uso de
ciertos conjuntos de caracteres. Las opciones de conexión deben ser establecidas antes de que se
establezcla una conexión de red.
Para configurar una opción de conexión, la operación de conexión ha de ser
realizada en tres pasos: crear un gestor de conexión con
mysqli_init, establecer las opciones solicitadas usando
mysqli_options, y establecer la conexión de red
con mysqli_real_connect.
Caché de conexiones
La extensión mysqli soporta conexiones persistentes a bases de datos, las cuales
son un tipo especial de conexiones almacenadas en caché. Por defecto, cada conexión
a una base de datos abierta por un script es cerrada explícitamente por el usuario durante
el tiempo de ejecución o liberada automáticamente al finalizar el script. Una conexión
persistente no. En su lugar, se coloca en una caché para su reutilización posterior, si
una conexión es abierta al mismo servidor usando el mismo nombre de usuario, contraseña, socket, puerto
y base de datos predeterminada. La reutilización ahorra gastos de conexioń.
Cada procesos de PHP utiliza su propia caché de conexiones mysqli.
Dependiendo de modelo de distribución del servidor web, un proceso PHP puede servir
una o múltiples peticiones. Por lo tanto, una conexión almacenada en caché puede ser
utilizada posteriormente por uno o más scripts.
Conexiones persistentes
Si una conexión persistente no usada con una combiación dada de host, nombre de usuario,
contraseña, socket, puerto y base de datos predeterminada no se puede encontrar en la caché de conexiones,
mysqli abrirá una nueva conexión. El uso de conexiones persistentes se puede
habilitar y deshabilitar usando la directiva de PHP mysqli.allow_persistent.
El número total de conexiones abiertas por un script puede ser limitado con
mysqli.max_links. El número máximo de conexiones persistentes
por proceso de PHP puede restringirse con mysqli.max_persistent.
Observe que el servidor web puede engendrar muchos procesos de PHP.
Una queja común sobre las conexiones persistentes qes que su estado no
es reiniciado antes de su uso. Por ejemplo, las transacciones abiertas y no finalizadas no son
automéáticamente reanudadas. También, los cambios de autorización que ocurran
durante la colocación de la conexión en la caché y su reutilización
no están reflejados. Esto puede verse como un efecto secundario no deseado. Al contrario,
el nombre persistente puede entenderse como una promesa
de que el estado persiste.
La extensión mysqli soporta ambas interpretaciones de una conexión persistente:
el estado persiste, y el estado se reinicia antes de la reutilización. Lo predeterminado es la reiniciación.
Antes de que una conexión sea reutilizada, la extensión llama
implicitamente a mysqli_change_user para reiniciar el estado. La
conexión persistente aparece al usuario como si estuviera recién abierta. No
son visibles ningún artefacto de los usos previos.
La función mysqli_change_user es una operación cara.
Para un mejor rendimiento, los usuarios pueden recompilar la extensión con la
bandera de compilación MYSQLI_NO_CHANGE_USER_ON_PCONNECT establecida.
Corresponde al usuario elegir entre comportamiento seguro o mejor rendimiento.
Ambas son metas de optimización válidas. Para facilitar el uso, el comportamiento seguro
es el predeterminado a expensas de un rendimiento máximo.
See alsomysqli::__constructmysqli::initmysqli::optionsmysqli::real_connectmysqli::change_user$mysqli::host_infoOpciones de configuración de MySQLiConexiones persistentes a bases de datosEjecutar sentencias
Las sentencias se pueden ejecutar con las funciones
mysqli_query, mysqli_real_query
y mysqli_multi_query.
La función mysqli_query es la más
común, y combina la sentencia de ejecución con su
conjunto de resultados obtenido de un buffer, si lo hubiera, en una llamada.
Llamar a mysqli_query es idéntico que
llamar a mysqli_real_query
seguido de mysqli_store_result.
Conectando a MySQL
connect_errno) {
echo "Falló la conexión con MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error;
}
if (!$mysqli->query("DROP TABLE IF EXISTS test") ||
!$mysqli->query("CREATE TABLE test(id INT)") ||
!$mysqli->query("INSERT INTO test(id) VALUES (1)")) {
echo "Falló la creación de la tabla: (" . $mysqli->errno . ") " . $mysqli->error;
}
?>
]]>
Conjuntos de resultados almacenados en buffer
Después de la ejecución de sentencias, los resultados pueden recuperarse de una sola vez para que sean almacenados en buffer
por el cliente o leyendo fila a fila. El almacenamieno en buffer de conjuntos de resultados en el lado del cliente
permite al servidor liberar recursos asociados con los resultados de la
sentencia tan pronto como sea posible. Generalmente hablando, los clientes son lentos
consumiendo conjuntos de resultados. Por lo tanto, se recomienda usar conjuntos de resultados
almacenados en buffer. mysqli_query combina la ejecución de
sentencias y el almacenamiento en buffer de conjuntos de resultados.
Las aplicaciones de PHP pueden navegar libremente a través de resultados almacenados en buffer.
La navegación es más rápida debido a que los conjuntos de resultados se mantienen en la memoria del cliente.
Tenga en cuenta que a menudo es más fácil procesar datos en el cliente que
hacerlo el servidor.
Navegación a través de resultados almacenados en buffer
connect_errno) {
echo "Falló la conexión a MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error;
}
if (!$mysqli->query("DROP TABLE IF EXISTS test") ||
!$mysqli->query("CREATE TABLE test(id INT)") ||
!$mysqli->query("INSERT INTO test(id) VALUES (1), (2), (3)")) {
echo "Falló la creación de la tabla: (" . $mysqli->errno . ") " . $mysqli->error;
}
$resultado = $mysqli->query("SELECT id FROM test ORDER BY id ASC");
echo "Orden inverso...\n";
for ($num_fila = $resultado->num_rows - 1; $num_fila >= 0; $num_fila--) {
$resultado->data_seek($num_fila);
$fila = $resultado->fetch_assoc();
echo " id = " . $fila['id'] . "\n";
}
echo "Orden del conjunto de resultados...\n";
$resultado->data_seek(0);
while ($fila = $resultado->fetch_assoc()) {
echo " id = " . $fila['id'] . "\n";
}
?>
]]>
&example.outputs;
Conjuntos de resultados no almacenados en buffer
Si la memoria del cliente es un recurso escaso y no es necesaria la liberación de recursos
del servidor tan pronto como sea posible para mantener la carga del servidor baja,
se pueden usar conjuntos de resultados no almacenados en buffer. Recorrer resultados no almacenados en buffer
no es posible antes de que todas las filas hayan sido leídas.
Navegación a través de resultados no almacenados en buffer
real_query("SELECT id FROM test ORDER BY id ASC");
$resultado = $mysqli->use_result();
echo "Orden del conjunto de resultados...\n";
while ($fila = $resultado->fetch_assoc()) {
echo " id = " . $fila['id'] . "\n";
}
?>
]]>
Tipos de datos de los valores del conjunto de resultados
Las funciones mysqli_query, mysqli_real_query
y mysqli_multi_query se usan para ejecutar
sentencias no preparadas. Al nivel del Protocolo Cliente Servidor de MySQL,
el comando COM_QUERY y el protocolo de texto se usan
para la ejecución de sentencias. Con el protocolo texto, el servidor MySQL convierte
todos los datos de los conjuntos de resultados en cadenas antes de enviarlos. Esta conversión se realiza
sin considerar el tipo de datos de las columnas del conjunto de resultados SQL. Las bibliotecas cliente de mysql
reciben todos los valores de las columnas como cadenas. No se realiza ninguna conversión del lado del cliente
pava volver a convertir las columnas a susu tipos nativos. En su lugar, todos los valores son
proporcionados como cadenas de PHP.
El protocolo de texto devuelve cadenas de manera predeterminado
connect_errno) {
echo "Falló la conexión a MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error;
}
if (!$mysqli->query("DROP TABLE IF EXISTS test") ||
!$mysqli->query("CREATE TABLE test(id INT, etiqueta CHAR(1))") ||
!$mysqli->query("INSERT INTO test(id, etiqueta) VALUES (1, 'a')")) {
echo "Falló la creación de la tabla: (" . $mysqli->errno . ") " . $mysqli->error;
}
$resultado = $mysqli->query("SELECT id, etiqueta FROM test WHERE id = 1");
$fila = $resultado->fetch_assoc();
printf("id = %s (%s)\n", $fila['id'], gettype($fila['id']));
printf("etiqueta = %s (%s)\n", $fila['etiqueta'], gettype($fila['etiqueta']));
?>
]]>
&example.outputs;
Es posible convertir valores de columnas de tipo integer y float a números de PHP estableciendo la
opción de conexión MYSQLI_OPT_INT_AND_FLOAT_NATIVE,
si se esta´utilizando la biblioteca mysqlnd. Si se establece, la biblioteca mysqlnd
comprobará los tipos de columna de los metadatos del conjunto de resultados y convertirá las columnas númerocas de SQL
a números de PHP, si el rango de valores del tipo de datos de PHP lo permite.
De esta forma, por ejemplo, las columnas INT de SQL son devueltas como enteros.
Tipos de datos nativos con mysqlnd y la opción de conexión
options(MYSQLI_OPT_INT_AND_FLOAT_NATIVE, 1);
$mysqli->real_connect("ejemplo.com", "user", "contraseña", "basedatos");
if ($mysqli->connect_errno) {
echo "Falló la conexión a MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error;
}
if (!$mysqli->query("DROP TABLE IF EXISTS test") ||
!$mysqli->query("CREATE TABLE test(id INT, etiqueta CHAR(1))") ||
!$mysqli->query("INSERT INTO test(id, etiqueta) VALUES (1, 'a')")) {
echo "Falló la creación de la tabla: (" . $mysqli->errno . ") " . $mysqli->error;
}
$resultado = $mysqli->query("SELECT id, etiqueta FROM test WHERE id = 1");
$fila = $resultado->fetch_assoc();
printf("id = %s (%s)\n", $fila['id'], gettype($fila['id']));
printf("etiqueta = %s (%s)\n", $fila['etiqueta'], gettype($fila['etiqueta']));
?>
]]>
&example.outputs;
Véase tambiénmysqli::__constructmysqli::initmysqli::optionsmysqli::real_connectmysqli::querymysqli::multi_querymysqli::use_resultmysqli::store_resultmysqli_result::freeSentencias Preparadas
Las bases de datos MySQL soportan sentencias preparadas. Una sentencia preparada
o una sentencia parametrizada se usa para ejecutar la misma sentencia
repetidamente con gran eficiencia.
Flujo de trabajo básico
La ejecución de sentencias preparadas consiste en dos etapas:
la preparación y la ejecución. En la etapa de preparación se envía una plantilla de sentencia
al servidor de bases de datos. El servidor realiza una comprobación de sintaxis e inicializa
los recursos internos del servidor para su uso posterior.
El servidor de MySQL soporta el uso de parámetros de sustitución posicionales anónimos
con ?.
Primera etapa: prepación
connect_errno) {
echo "Falló la conexión a MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error;
}
/* Sentencia no preparada */
if (!$mysqli->query("DROP TABLE IF EXISTS test") || !$mysqli->query("CREATE TABLE test(id INT)")) {
echo "Falló la creación de la tabla: (" . $mysqli->errno . ") " . $mysqli->error;
}
/* Sentencia preparada, etapa 1: preparación */
if (!($sentencia = $mysqli->prepare("INSERT INTO test(id) VALUES (?)"))) {
echo "Falló la preparación: (" . $mysqli->errno . ") " . $mysqli->error;
}
?>
]]>
La preparación es seguida de la ejecución. Durante la ejecución el cliente vincula
los valores de los parámetros y los envía al servidor. El servidor crea una
sentencia desde la plantilla de la sentencia y los valores vinculados para
ejecutarla usando los recursos internos previamente creados.
Segunda etapa: vincular y ejecutar
bind_param("i", $id)) {
echo "Falló la vinculación de parámetros: (" . $sentencia->errno . ") " . $sentencia->error;
}
if (!$sentencia->execute()) {
echo "Falló la ejecución: (" . $sentencia->errno . ") " . $sentencia->error;
}
?>
]]>
Ejecución repetida
Una sentencia preparada se puede ejecutar repetidamente. En cada ejecución
el valor actual de la variable vinculada se evalúa y se envía al servidor.
La sentencia no se analiza de nuevo. La plantilla de la sentencia no
es transferida otra vez al servidor.
INSERT preparada una vez, ejecutada múltiples veces
connect_errno) {
echo "Falló la conexión a MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error;
}
/* Sentencia no preparada */
if (!$mysqli->query("DROP TABLE IF EXISTS test") || !$mysqli->query("CREATE TABLE test(id INT)")) {
echo "Falló la creación de la tabla: (" . $mysqli->errno . ") " . $mysqli->error;
}
/* Sentencia preparada, etapa 1: preparación */
if (!($sentencia = $mysqli->prepare("INSERT INTO test(id) VALUES (?)"))) {
echo "Falló la preparación: (" . $mysqli->errno . ") " . $mysqli->error;
}
/* Sentencia preparada, etapa 2: vinculación y ejecución */
$id = 1;
if (!$sentencia->bind_param("i", $id)) {
echo "Falló la vinculación de parámetros: (" . $sentencia->errno . ") " . $sentencia->error;
}
if (!$sentencia->execute()) {
echo "Falló la ejecución: (" . $sentencia->errno . ") " . $sentencia->error;
}
/* Sentencia preparada: ejecución repetida, sólo datos transferidos desde el cliente al servidor */
for ($id = 2; $id < 5; $id++) {
if (!$sentencia->execute()) {
echo "Falló la ejecución: (" . $sentencia->errno . ") " . $sentencia->error;
}
}
/* se recomienda el cierre explícito */
$sentencia->close();
/* Sentencia no preparada */
$resultado = $mysqli->query("SELECT id FROM test");
var_dump($resultado->fetch_all());
?>
]]>
&example.outputs;
array(1) {
[0]=>
string(1) "1"
}
[1]=>
array(1) {
[0]=>
string(1) "2"
}
[2]=>
array(1) {
[0]=>
string(1) "3"
}
[3]=>
array(1) {
[0]=>
string(1) "4"
}
}
]]>
Cada sentencia preparada ocupa recursos del servidor.
Las sentencias deberían cerrarse explícitamente inmediatamente después de su uso.
Si no se realiza explícitamente, la sentencia será cerrada cuando
el gestor de la sentencia sea liberado por PHP.
Usar una sentencia preparada no es siempre la manera más
eficiente de ejecutar una sentencia. Una sentencia preparada ejecutada una sola
vez causa más viajes de ida y vuelta desde el cliente al servidor que una sentencia no preparada.
Es por esta razón por lo que SELECT no se ejecuta arriba como
una sentencia preparada.
También se ha de considerar el uso de la sintaxis SQL multi-INSERT de MySQL para sentencias INSERT.
Por ejemplo, multi-INSERT requiere menos viajes de ida y vuelta entre
el servidor y el cliente que la sentencia preparada mostrada arriba.
Menos viajes de ida y vuelta usando SQL multi-INSERT
query("INSERT INTO test(id) VALUES (1), (2), (3), (4)")) {
echo "Falló multi-INSERT: (" . $mysqli->errno . ") " . $mysqli->error;
}
?>
]]>
Tipos de datos de los valores del conjunto de resultados
El Protocolo Cliente Servidor de MySQL define un protocolo de transporte de datos diferente
para sentencias preparadas y no preparadas. Las sentencias preparadas
usan el llamado protocolo binario. El servidor de MySQL envía los datos
del conjunto de resultados "tal cual" en formato binario. Los resultados no son serializados en
cadenas antes del envío. Las bibliotecas cliente no reciben cadenas solamente.
En su lugar, recibirán datos binarios e intentarán convertir los valores a
los tipos de datos de PHP apropiados. Por ejemplo, los resultados de una columna
de SQL INT serán proporcionados como variables de tipo integer de PHP.
Tipos de datos nativos
connect_errno) {
echo "Falló la conexión a MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error;
}
if (!$mysqli->query("DROP TABLE IF EXISTS test") ||
!$mysqli->query("CREATE TABLE test(id INT, etiqueta CHAR(1))") ||
!$mysqli->query("INSERT INTO test(id, etiqueta) VALUES (1, 'a')")) {
echo "Falló la creación de la tabla: (" . $mysqli->errno . ") " . $mysqli->error;
}
$sentencia = $mysqli->prepare("SELECT id, etiqueta FROM test WHERE id = 1");
$sentencia->execute();
$resultado = $sentencia->get_result();
$fila = $resultado->fetch_assoc();
printf("id = %s (%s)\n", $fila['id'], gettype($fila['id']));
printf("etiqueta = %s (%s)\n", $fila['etiqueta'], gettype($fila['etiqueta']));
?>
]]>
&example.outputs;
Este comportamiento difiere de las sentencias no preparadas. Por defecto,
las sentencias no preparadas devolverán todos los resultados como cadenas.
Este comportamiento predeterminado se puede cambiar usando una opción de conexión.
Si se utiliza la opción de conexión no existirán diferencias.
Obtener resultados usando variables vinculadas
Los resultados de las sentencias preparadas pueden ser recuperados mediante
varibles de salida vinculadas, o por la petición de un objeto mysqli_result.
Las variables de salida deben ser vinculadas después de la ejecución de la sentencia.
Una variable debe estar vinculada para cada columna del conjunto de resultados de las sentencias.
Vinculación de variables de salida
connect_errno) {
echo "Falló la conexión a MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error;
}
if (!$mysqli->query("DROP TABLE IF EXISTS test") ||
!$mysqli->query("CREATE TABLE test(id INT, etiqueta CHAR(1))") ||
!$mysqli->query("INSERT INTO test(id, etiqueta) VALUES (1, 'a')")) {
echo "Falló la creación de la tabla: (" . $mysqli->errno . ") " . $mysqli->error;
}
if (!($sentencia = $mysqli->prepare("SELECT id, etiqueta FROM test"))) {
echo "Falló la preparación: (" . $mysqli->errno . ") " . $mysqli->error;
}
if (!$sentencia->execute()) {
echo "Falló la ejecución: (" . $mysqli->errno . ") " . $mysqli->error;
}
$id_salida = NULL;
$etiqueta_salida = NULL;
if (!$sentencia->bind_result($id_salida, $etiqueta_salida)) {
echo "Falló la vinculación de los parámetros de salida: (" . $sentencia->errno . ") " . $sentencia->error;
}
while ($sentencia->fetch()) {
printf("id = %s (%s), etiqueta = %s (%s)\n", $id_salida, gettype($id_salida), $etiqueta_salida, gettype($etiqueta_salida));
}
?>
]]>
&example.outputs;
Las sentencias preparadas devuelven de manera predeterminada conjuntos de resultados no almacenados en buffer.
Los resultados de la sentencia no son obtenidas y transferidas implícitamente
desde el servidor al cliente para el almacenamiento en buffer de lado del cliente. El conjunto de resultados
toma recursos del servidor hasta que todos los resultados hayan sido obtenidos por el cliente.
Por lo que se recomienda consumir resultados cuando sea oportuno. Si un cliente falla al obtener todos
los resultados o el cliente cierra la consulta antes de haber obtenido todos los datos,
los datos han de ser obtenidos implícitamente por mysqli.
También es posible almacenar en buffer los resutados de una sentencia preparada
usando mysqli_stmt_store_result.
Obtener los resultados usando la interfaz mysqli_result
En lugar de usar resultados vinculados, los resultados también se pueden recuperar a través de la
interfaz mysqli_result. mysqli_stmt_get_result
devuelve un conjunto de resultados almacenado en buffer.
Usar mysqli_result para obtener los resultados
connect_errno) {
echo "Falló la conexión a MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error;
}
if (!$mysqli->query("DROP TABLE IF EXISTS test") ||
!$mysqli->query("CREATE TABLE test(id INT, etiqueta CHAR(1))") ||
!$mysqli->query("INSERT INTO test(id, etiqueta) VALUES (1, 'a')")) {
echo "Falló la creación de la tabla: (" . $mysqli->errno . ") " . $mysqli->error;
}
if (!($sentencia = $mysqli->prepare("SELECT id, etiqueta FROM test ORDER BY id ASC"))) {
echo "Falló la preparación: (" . $mysqli->errno . ") " . $mysqli->error;
}
if (!$sentencia->execute()) {
echo "Falló la ejecución: (" . $sentencia->errno . ") " . $sentencia->error;
}
if (!($resultado = $sentencia->get_result())) {
echo "Falló la obtención del conjunto de resultados: (" . $sentencia->errno . ") " . $sentencia->error;
}
var_dump($resultado->fetch_all());
?>
]]>
&example.outputs;
array(2) {
[0]=>
int(1)
[1]=>
string(1) "a"
}
}
]]>
Usar la interfaz mysqli_result ofrece el beneficio adicional de
la navegación flexible del conjunto de resultados en el lado del cliente.
Conjunto de resultados almacenado en buffer para la lectura flexible
connect_errno) {
echo "Falló la conexión a MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error;
}
if (!$mysqli->query("DROP TABLE IF EXISTS test") ||
!$mysqli->query("CREATE TABLE test(id INT, etiqueta CHAR(1))") ||
!$mysqli->query("INSERT INTO test(id, etiqueta) VALUES (1, 'a'), (2, 'b'), (3, 'c')")) {
echo "Falló la creación de la tabla: (" . $mysqli->errno . ") " . $mysqli->error;
}
if (!($sentencia = $mysqli->prepare("SELECT id, etiqueta FROM test"))) {
echo "Falló la preparación: (" . $mysqli->errno . ") " . $mysqli->error;
}
if (!$sentencia->execute()) {
echo "Falló la ejecución: (" . $sentencia->errno . ") " . $sentencia->error;
}
if (!($resultado = $sentencia->get_result())) {
echo "Falló la obtención del conjunto de resultados: (" . $sentencia->errno . ") " . $sentencia->error;
}
for ($num_fila = ($resultado->num_rows - 1); $num_fila >= 0; $num_fila--) {
$resultado->data_seek($num_fila);
var_dump($resultado->fetch_assoc());
}
$resultado->close();
?>
]]>
&example.outputs;
int(3)
["etiqueta"]=>
string(1) "c"
}
array(2) {
["id"]=>
int(2)
["etiqueta"]=>
string(1) "b"
}
array(2) {
["id"]=>
int(1)
["etiqueta"]=>
string(1) "a"
}
]]>
Escape de valores e inyección SQL
Las variables vinculadas son enviadas desde la consulta al servidor por separado, por lo que
no se puede interferir. El servidor usa estos valores directamente en el momento
de la ejecución, después de haber analizado la plantilla de la sentencia. Los parámetros vinculados no
necesitan ser escapados porque nunca son sustituidos directamente dentro del string de
consulta. Se puede proporcionar una sugerencia al servidor para el tipo de variable
vinculada, para crear una conversión apropiada.
Véase la función mysqli_stmt_bind_param para más
información.
A veces, tal separación es considerada como la única característica de seguridad para
evitar inyecciones SQL, pero se puede alcanzar el mismo grado de seguridad con
sentencias no preparadas, si todos los valores están formateados correctamente. Debería
observarse que el formateo correcto no es lo mismo que usar escapes, y que involucra
más lógica que simplemente usar escapes. Por lo tanto, las sentencias preparadas son sencillamente una
estrategia más conveniente y menos propensa a errores para este elemetno de seguridad de bases de datos.
Emulación de sentencias preparadas en el lado del cliente
La API no inclye la emulación para sentencias preparadas en el lado del cliente.
Comparación entre sentencias preparadas y no preparadas
La tabla de abajo compara las sentencias preparadas y no preparadas del lado del servidor.
Comparación entre sentencias preparadas y no preparadasSentencia preparadaSentencia no preparadaViajes de ida y vuelta desde el cliente al servidor, SELECT, ejecución única21Cadenas de sentencias tranferidas desde el cliente al servidor11Viajes de ida y vuelta desde el cliente al servidor, SELECT, ejecución repetida (n)1 + nnCadenas de sentencias tranferidas desde el cliente al servidor1 plantilla, n veces parametro vinculado, si existen veces junto con el parámetro, si existeAPI de vinculación de parámetros de entradaSí, escape de entradas automáticoNo, escape de entradas manualAPI de vinculación de variables de salidaSíNoSoporte para el uso de la API mysqli_resultSí, use mysqli_stmt_get_resultSíConjuntos de resultados almacenados en buffer
Sí, use mysqli_stmt_get_result o
la vinculación con mysqli_stmt_store_resultSí, lo predeterminado de mysqli_queryConjuntos de resultados no almacenados en bufferSí, use la API de vinculación de salida
Sí, use mysqli_real_query con
mysqli_use_result"Sabor" de la transferencia de datos del protocolo Cliente Servidor de MySQLProtocolo binarioProtocolo de textoTipos de datos SQL de los valores del conjunto de resultadosPreservados al obtenerlosConvertidos a cadena o preservados al obetenerlosSoporta todas las sentencia SQLVersiones reciente de MySQL soportan muchas pero no todasSí
Véase tambiénmysqli::__constructmysqli::querymysqli::preparemysqli_stmt::preparemysqli_stmt::executemysqli_stmt::bind_parammysqli_stmt::bind_resultProcedimientos almacenados
Las bases de datos de MySQL soportan procedimientos almacenados. Un procedimiento almacenado es una
subrutina almacenada en el catálogo de la base de datos. Las aplicaciones pueden llamar y
ejecutar el procedimiento almacenado. La sentencia de SQL CALL
se usa para ejecutar un procedimiento almacenado.
Parámetros
Los procedimientos almacenados pueden tener parámetros IN,
INOUT y OUT,
dependiendo de la versión de MySQL. La interfaz mysqli no tiene una noción
especial de los diferentes tipos de parámetros.
Parámetro IN
Los parámetros de entrada son proporcionados con la sentencia CALL.
Asegúrese de que los valores están escapados correctamente.
Llamar a un procedimiento almacenado
connect_errno) {
echo "Falló la conexión a MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error;
}
if (!$mysqli->query("DROP TABLE IF EXISTS test") || !$mysqli->query("CREATE TABLE test(id INT)")) {
echo "Falló la creación de la tabla: (" . $mysqli->errno . ") " . $mysqli->error;
}
if (!$mysqli->query("DROP PROCEDURE IF EXISTS p") ||
!$mysqli->query("CREATE PROCEDURE p(IN id_val INT) BEGIN INSERT INTO test(id) VALUES(id_val); END;")) {
echo "Falló la creación del procedimiento almacenado: (" . $mysqli->errno . ") " . $mysqli->error;
}
if (!$mysqli->query("CALL p(1)")) {
echo "Falló CALL: (" . $mysqli->errno . ") " . $mysqli->error;
}
if (!($resultado = $mysqli->query("SELECT id FROM test"))) {
echo "Falló SELECT: (" . $mysqli->errno . ") " . $mysqli->error;
}
var_dump($resultado->fetch_assoc());
?>
]]>
&example.outputs;
string(1) "1"
}
]]>
Parámetro INOUT/OUT
A los valores de los parámetros INOUT/OUT
se acceden usando variables de sesión.
Usar variables de sesión
connect_errno) {
echo "Falló la conexión a MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error;
}
if (!$mysqli->query("DROP PROCEDURE IF EXISTS p") ||
!$mysqli->query('CREATE PROCEDURE p(OUT msg VARCHAR(50)) BEGIN SELECT "¡Hola!" INTO msg; END;')) {
echo "Falló la creación del procedimiento almacenado: (" . $mysqli->errno . ") " . $mysqli->error;
}
if (!$mysqli->query("SET @msg = ''") || !$mysqli->query("CALL p(@msg)")) {
echo "Falló CALL: (" . $mysqli->errno . ") " . $mysqli->error;
}
if (!($resultado = $mysqli->query("SELECT @msg as _p_out"))) {
echo "Falló la obtención: (" . $mysqli->errno . ") " . $mysqli->error;
}
$fila = $resultado->fetch_assoc();
echo $fila['_p_out'];
?>
]]>
&example.outputs;
Los desarrolladores de aplicaciones y framework pueden proporcionar una API más
conveniente usando una mezcla de variables de sesiones e inspección del catálogo de la base de datos.
Sin embargo, observe el posible impacto de rendimiento de una solución
personalizada basada en la inspección del catálogo.
Manejar conjuntos de resultados
Los procedimientos almacenados pueden devolver conjuntos de resultados. Los conjuntos de resultados devueltos desde
un procedimiento almacenado no se pueden obtener correctamente usando mysqli_query.
La función mysqli_query combina la ejecución de la sentencia
y la obtención del primer conjunto de resultados dentro de un conjunto de resultados almacenado en buffer, si existe.
Sin embargo, existen unos conjuntos de resultados del procedimiento almacenado ocultos
para el usuario que hacen que mysqli_query falle
al devolver los conjuntos de resultados esperados por el usuario.
Los conjuntos de resultados devueltos desde un procedimiento almacenado son obtenidos usando
mysqli_real_query o mysqli_multi_query.
Ambas funciones permiten la obtención de cualquier número de conjuntos de resultados devueltos
por una sentencia, como CALL. El fallo de la obtención de todos
los conjuntos de resultados devueltos por un procedimiento almacenado causa un error.
Obtener los resultados de procedimientos almacenados
connect_errno) {
echo "Falló la conexión a MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error;
}
if (!$mysqli->query("DROP TABLE IF EXISTS test") ||
!$mysqli->query("CREATE TABLE test(id INT)") ||
!$mysqli->query("INSERT INTO test(id) VALUES (1), (2), (3)")) {
echo "Falló la creación de la tabla: (" . $mysqli->errno . ") " . $mysqli->error;
}
if (!$mysqli->query("DROP PROCEDURE IF EXISTS p") ||
!$mysqli->query('CREATE PROCEDURE p() READS SQL DATA BEGIN SELECT id FROM test; SELECT id + 1 FROM test; END;')) {
echo "Falló la creación del procedimiento almacenado: (" . $mysqli->errno . ") " . $mysqli->error;
}
if (!$mysqli->multi_query("CALL p()")) {
echo "Falló CALL: (" . $mysqli->errno . ") " . $mysqli->error;
}
do {
if ($resultado = $mysqli->store_result()) {
printf("---\n");
var_dump($resultado->fetch_all());
$resultado->free();
} else {
if ($mysqli->errno) {
echo "Store failed: (" . $mysqli->errno . ") " . $mysqli->error;
}
}
} while ($mysqli->more_results() && $mysqli->next_result());
?>
]]>
&example.outputs;
array(1) {
[0]=>
string(1) "1"
}
[1]=>
array(1) {
[0]=>
string(1) "2"
}
[2]=>
array(1) {
[0]=>
string(1) "3"
}
}
---
array(3) {
[0]=>
array(1) {
[0]=>
string(1) "2"
}
[1]=>
array(1) {
[0]=>
string(1) "3"
}
[2]=>
array(1) {
[0]=>
string(1) "4"
}
}
]]>
Uso de sentencias preparadas
No es necesario un trato especial al usar la interfaz de sentencias
preparadas para obtener los resultados del mismo procedimiento almacenado de arriba.
Las interfaces de sentencias preparadas y no preparadas son similares.
Obserque que no todas las versioines del servidor de MYSQL pueden soportar
la preparación de la sentencia SQL CALL.
Procedimientos Almacenados y Sentencias Preparadas
connect_errno) {
echo "Falló la conexión a MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error;
}
if (!$mysqli->query("DROP TABLE IF EXISTS test") ||
!$mysqli->query("CREATE TABLE test(id INT)") ||
!$mysqli->query("INSERT INTO test(id) VALUES (1), (2), (3)")) {
echo "Falló la creación de la tabla: (" . $mysqli->errno . ") " . $mysqli->error;
}
if (!$mysqli->query("DROP PROCEDURE IF EXISTS p") ||
!$mysqli->query('CREATE PROCEDURE p() READS SQL DATA BEGIN SELECT id FROM test; SELECT id + 1 FROM test; END;')) {
echo "Falló la creación del procedimiento almacenado: (" . $mysqli->errno . ") " . $mysqli->error;
}
if (!($sentencia = $mysqli->prepare("CALL p()"))) {
echo "Falló la preparación: (" . $mysqli->errno . ") " . $mysqli->error;
}
if (!$sentencia->execute()) {
echo "Falló la ejecución: (" . $sentencia->errno . ") " . $sentencia->error;
}
do {
if ($resultado = $sentencia->get_result()) {
printf("---\n");
var_dump(mysqli_fetch_all($resultado));
mysqli_free_result($resultado);
} else {
if ($sentencia->errno) {
echo "Store failed: (" . $sentencia->errno . ") " . $sentencia->error;
}
}
} while ($sentencia->more_results() && $sentencia->next_result());
?>
]]>
Por supuesto, tamibién está soportado el uso de la API de vinculación para la obtención.
Procedimientos Almacenados y Sentencias Preparadas usando la API de vinculación
prepare("CALL p()"))) {
echo "Falló la preparación: (" . $mysqli->errno . ") " . $mysqli->error;
}
if (!$sentencia->execute()) {
echo "Falló la ejecución: (" . $sentencia->errno . ") " . $sentencia->error;
}
do {
$id_out = NULL;
if (!$sentencia->bind_result($id_out)) {
echo "Falló la vinculiación: (" . $sentencia->errno . ") " . $sentencia->error;
}
while ($sentencia->fetch()) {
echo "id = $id_out\n";
}
} while ($sentencia->more_results() && $sentencia->next_result());
?>
]]>
Véase tambiénmysqli::querymysqli::multi_querymysqli_result::next-resultmysqli_result::more-resultsSentencias Múltiples
MySQL permite opcionalmente tener múltiples sentencias en una cadena de sentencias.
El envío de múltiples sentencias de una sola vez reduce los viajes de ida y vuelta desde
el cliente al servidor, pero requiere un manejo especial.
Las sentencias múltiples o multiconsultas deben ser ejecutadas
con mysqli_multi_query. Las sentencias individuales
de la cadena de sentencias están serparadas por un punto y coma.
Entonces, todos los conjuntos de resultados devueltos por las sentencias ejecutadas deben ser obtenidos.
El servidor MySQL permite tener sentencias que devuelven conjuntos de resultados y
sentencias que no devuelve conjuntos de resultados en una sentencia múltiple.
Sentencias múltiples
connect_errno) {
echo "Falló la conexión a MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error;
}
if (!$mysqli->query("DROP TABLE IF EXISTS test") || !$mysqli->query("CREATE TABLE test(id INT)")) {
echo "Falló la creación de la tabla: (" . $mysqli->errno . ") " . $mysqli->error;
}
$sql = "SELECT COUNT(*) AS _num FROM test; ";
$sql.= "INSERT INTO test(id) VALUES (1); ";
$sql.= "SELECT COUNT(*) AS _num FROM test; ";
if (!$mysqli->multi_query($sql)) {
echo "Falló la multiconsulta: (" . $mysqli->errno . ") " . $mysqli->error;
}
do {
if ($resultado = $mysqli->store_result()) {
var_dump($resultado->fetch_all(MYSQLI_ASSOC));
$resultado->free();
}
} while ($mysqli->more_results() && $mysqli->next_result());
?>
]]>
&example.outputs;
array(1) {
["_num"]=>
string(1) "0"
}
}
array(1) {
[0]=>
array(1) {
["_num"]=>
string(1) "1"
}
}
]]>
Consideraciones de seguridad
Las funciones de la API mysqli_query y
mysqli_real_query no establecen una bandera de conexión necesaria
para activar las multiconsultas en el servidor. Se usa una llamada extra a la API para
las sentencias múltiples para reducir la verosimilitud de los ataques de inyecciones SQL
accidentales. Un atacante puede intentar añadir sentencias como
; DROP DATABASE mysql o ; SELECT SLEEP(999).
Si el atacante tiene éxito al añadir SQL a la cadena de sentencias pero
no se usa mysqli_multi_query, el servidor no
ejecutará la segunda sentencia SQL inyectada y maliciosa.
Inyección SQL
query("SELECT 1; DROP TABLE mysql.user");
if (!$resultado) {
echo "Error al ejecutar la consulta: (" . $mysqli->errno . ") " . $mysqli->error;
}
?>
]]>
&example.outputs;
Sentencias preparadas
El uso de sentencias múltiples con sentencias preparadas no está soportado.
See alsomysqli::querymysqli::multi_querymysqli_result::next-resultmysqli_result::more-resultsSoporte de la API para transacciones
El servidor MySQL soporta transacciones dependiendo de del motor de almacenamiento usado.
Desde MySQL 5.5, el motor de almacenamiento predeterminado es InnoDB.
InnoDB tiene soporte completo para transacciones ACID.
Las transacciones se pueden controlar usando SQL o llamadas a la API.
Se recomienda usar llamadas a la API para habilitar y deshabilitar el
modo de autoconsignación (auto commit) y para consignar y reiniciar transacciones.
Establecer el modo de autoconsignación con SQL y a través de la API
connect_errno) {
echo "Falló la conexión a MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error;
}
/* Recomendado: usar la API para cotrolar las configuraciones transaccionales */
$mysqli->autocommit(false);
/* No serán monitorizadas y reconocidas por la aplicación y el complemento de balance de carga */
if (!$mysqli->query('SET AUTOCOMMIT = 0')) {
echo "Falló la consulta: (" . $mysqli->errno . ") " . $mysqli->error;
}
?>
]]>
Los paquetes de características opcionales, como los comlementos de replicación y el de balance de carga,
pueden fácilmente monitorizar llamadas a la API. El complemento de replicación transacciones
conscientes del balance de carga, si las transacciones están controladas con llamadas a la API.
Las transacciones conscientes del balance de carga no están disponibles si las sentencias SQL se
usan para establecer el modo de autoconsignación, consignación y reinicio de una transacción.
Consignación y reinicio
autocommit(false);
$mysqli->query("INSERT INTO test(id) VALUES (1)");
$mysqli->rollback();
$mysqli->query("INSERT INTO test(id) VALUES (2)");
$mysqli->commit();
?>
]]>
Observe que el servidor MySQL no puede reiniciar todas las sentencias.
Algunoas sentencias causan una consignación implícia.
Véase tambiénmysqli::autocommitmysqli_result::commitmysqli_result::rollbackMetadatos
Un conjunto de resultados de MySQL contiene metadatos. Los metadatos describen las columnas
encontradas en el conjunto de resultados. Todos los metadatos enviados por MySQL son accesibles
a través de la interfaz de mysqli.
La extensión realiza cambios insignificantes o no realiza ninguno a la
información que recibe.
La diferencias entre versiones del servidor MySQL no están alineadas.
A los metadatos se puede acceder a través de la interfaz mysqli_result.
Acceder a los metadatos de un conjunto de resultados
connect_errno) {
echo "Falló la conexión a MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error;
}
$resultado = $mysqli->query("SELECT 1 AS _one, 'Hello' AS _two FROM DUAL");
var_dump($resultado->fetch_fields());
?>
]]>
&example.outputs;
object(stdClass)#3 (13) {
["name"]=>
string(4) "_one"
["orgname"]=>
string(0) ""
["table"]=>
string(0) ""
["orgtable"]=>
string(0) ""
["def"]=>
string(0) ""
["db"]=>
string(0) ""
["catalog"]=>
string(3) "def"
["max_length"]=>
int(1)
["length"]=>
int(1)
["charsetnr"]=>
int(63)
["flags"]=>
int(32897)
["type"]=>
int(8)
["decimals"]=>
int(0)
}
[1]=>
object(stdClass)#4 (13) {
["name"]=>
string(4) "_two"
["orgname"]=>
string(0) ""
["table"]=>
string(0) ""
["orgtable"]=>
string(0) ""
["def"]=>
string(0) ""
["db"]=>
string(0) ""
["catalog"]=>
string(3) "def"
["max_length"]=>
int(5)
["length"]=>
int(5)
["charsetnr"]=>
int(8)
["flags"]=>
int(1)
["type"]=>
int(253)
["decimals"]=>
int(31)
}
}
]]>
Sentencias preparadas
A los metadatos de un conjunto de resultados creado usando sentencias preparadas se accede
de la misma manera. Un gestor de mysqli_result apropiado es
devuelto por mysqli_stmt_result_metadata.
Metadatos de sentencias preparadas
prepare("SELECT 1 AS _one, 'Hello' AS _two FROM DUAL");
$sentencia->execute();
$resultado = $sentencia->result_metadata();
var_dump($resultado->fetch_fields());
?>
]]>
Véase tambiénmysqli::querymysqli_result::fetch_fields