Ejecución de sentencias SQL en C

Ahora ya podemos conectarnos a una base de datos y gestionar los errores correctamente, de manera que ha llegado la hora de que su programa realice un trabajo real. La función API principal para ejecutar las sentencias SQL es:

sentencias_sql_1

Esta rutina adopta el indicador de la estructura de conexión y algún SQL válido en modo de cadena de texto (sin el punto y coma final, como en la herramienta mysql). Si tiene éxito recibirá un cero. Se puede usar una segunda rutina, mysql_real_query, si se solicitan datos binarios, pero en este capítulo vamos a usar solo mysql_query.

Sentencias SQL que no devuelven datos

Simplificando, observaremos algunas sentencias SQL que no envían ningún dato:

UPDATE, DELETE e INSERT.

Veamos otra función importante que es la encargada de comprobar el número de filas a las que afecta la consulta:

sentencias_1

Debemos tener en cuenta el tipo de datos poco común que envía esta función. Se trata de un tipo sin firma que se usa por cuestiones de portabilidad. Cuando esté usando printf, se recomienda su emisión sin firmar junto con un formato de %lu. Esta función envía el número de líneas a las que afectan las consultas UPDATE, INSERT y DELETE emitidas previamente. Dicho valor de retorno podría cogernos desprevenidos si previamente hemos trabajado con otras bases de datos SQL. MySQL envía el numero de filas modificado por una actualización, mientras que la mayoría de bases de datos tendrían en cuenta un registro actualizado, únicamente porque se corresponde con cualquier cláusula WHERE.

Generalmente, con la funciones mysql_, un retorno de 0 indica que no ha afectado a ninguna fila y un número positivo es el resultado real, normalmente el número de filas afectadas por la sentencia.

 Lo primero que tenemos que hacer es crear una tabla de ALUMNOS en la base de datos loquesea, si aún no lo ha echo. Elimine (usando el comando drop) cualquier tabla existente para asegurar que dispone de una definición de tabla totalmente vacía, y para volver a enviar cualquier ID usada en la columna AUTO_INCREMENT:

 SENTENCIAS_SALIDA1Luego añadimos el listado del archivo crea_alumnos.sql que hemos creado previamente:

mysql_paso6

Dentro de la consola de comandos del MySQL escribimos:

mysql_paso8

Añadimos ahora código a connect2.c con el fin de insertar una nueva fila en la tabla. Llamaremos  este nuevo programa insert1.c.

listado_sentencias

Si lo compilamos y ejecutamos tendremos la salida:

salida_insert_1

y desde la consola MySQL:

insert_salida_2

 Vemos que la línea se a insertado satisfactoriamente.

Ahora modificaremos el código para incluir un UPDATE, en vez de INSERT, y verá cómo se informa de las líneas afectadas:

update_listado

Llame a este programa update1.c. Éste intenta establecer la edad de todas los alumnas que se llamen Sara en 19 años:

SELECT_22

 Descubrir lo que hemos insertado

Tenemos un pequeño aspecto crucial a tener en cuenta en lo relativo a la inserción de datos. Si miramos al pasado de este curso/tutorial, mencionamos un tipo de columna llamada AUTO_INCREMENT, que usaba MySQL para asignar una ID automática. Esta prestación es realmente muy útil, sobre todo si existen varios usuarios.

Veamos nuevamente la definición de la tabla:

crear_tabla_children

Vemos. que la columna childno es un campo AUTO_INCREMENT.

Todo correcto, pero ¿como sabremos que número se ha asignado cada vez que ponemos un dato? Podemos ejecutar la sentencia SELECT (tal como vemos en la ilustración de abajo) para recuperar los datos, buscando el nombre del niño pero es poco eficiente, y no se garantiza que sea el único (posiblemente existan dos niños con el mismo nombre). O podemos encontrarnos que varios usuarios inserten al mismo tiempo, y se podrían insertar, por tanto, otras filas entre su actualización y su sentencia SELECT.

children_1

Como podríamos descubrir el valor de una columna AUTO_INCREMENT suele ser un problema común, MySQL proporciona una solución especial a modo de función LAST_INSERT_ID(). Siempre que MySQL inserta un valor de datos en una columna AUTO_INCREMENT, se conserva, basándose en el usuario, el último valor asignado. Los programas de los usuarios pueden recuperar este valor seleccionado (mediante SELECT) la función un poco especial LAST_INSERT_ID(), que actúa de algún modo como una pseudo columna.

 Recuperar la ID generada por AUTO_INCREMENT

Vamos a ver como funciona insertando algunos valores en nuestra tabla,

insert_timoteoy usando después la función LAST_INSERT_ID().

last_insert_id

Insertemos otro registro:

OTRO_INSERT

Cómo funciona

auto_increment_2

Si desea experimentar para comprobar que el número enviado es, de hecho, único para su sesión, abra una sesión diferente e inserte otra fila. En la sesión original, vuelva a ejecutar la sentencia SELECT LAST_INSERT_ID();. Verá que el número no ha variado porque el número enviado es el último introducido por la sesión actual. Sin embargo, si ejecuta SELECT * FROM, verá que la otra sesión sí que ha insertado datos.

usuarios_juntos

Uso de ID automáticas a partir de un programa C

En este ejemplo, vamos a modificar el programa insert1.c para ver como trabaja en C. Llamemos a este programa insert2.c:

sentencias32

La salida sería:

insert2

 Sentencias que retornan datos

El uso más frecuente de SQL es, por supuesto, recuperar más que insertar o actualizar los datos. Los datos se recuperan con la sentencia SELECT.

sentencias_nota

La recuperación de datos en su aplicación C se hace en cuatro pasos:

  1. Emitir la consulta.
    • Igual que hicimos con INSERT y DELETE, usará mysql_query para enviar SQL.
  2. Recuperar los datos.
    • A continuación recuperará los datos usando mysql_store_result o mysql_use_result (dependiendo de cómo quiera recuperar dichos datos).
  3. Procesar los datos.
    • Posteriormente usará una secuencia de llamadas mysql_fetch_row para procesar los datos. Por último, usará mysql_free_result para vaciar la memoria que usó para realizar la petición. La diferencia entre mysql_use_result y mysql_store_result reside básicamente en si deseamos obtener los datos fila por fila o todo junto.
  4. Organizarlos si se necesita.
    • El último es más apropiado si creemos que los resultados van a ser un conjunto de resultados más pequeños.

Funciones para recuperar todos los datos a la vez

Podemos recuperar todos los datos a partir de SELECT (o cualquier otra sentencia que recupere datos), a través de una única llamada, usando mysql_store_result:

 mysql_store_resultEvidentemente, querrá usar esta función tras una llamada con éxito a mysql_querry. La función almacenará todos los datos retornados al cliente inmediatamente. Enviará un indicador de posición de una nueva estructura denominada estructura de conjunto de resultados, o NULL si la sentencia falla. Si tiene éxito, a continuación llamará a mysql_num_rows para obtener el número de registros retornados, que esperamos sea un número positivo (puede ser cero si no se ha enviado ninguna fila).

mysql_num_rows

Adopta la estructura de resultado enviada desde mysql_store_result y envía el número de filas en dicho conjunto de resultados. Siempre que mysql_store_result tenga éxito, mysql_num_rows tendrá éxito. Esta combinación de funciones es un modo sencillo para recuperar los datos necesarios. En este momento,  todos los datos se encuentran en el entorno local del cliente y ya no tendrá que preocuparse por los errores de red o de base de datos. Mediante la obtención del número de filas retornadas, facilitará la codificación que queda por hacer.

 Si estamos trabajando con un conjunto de datos bastante grande, sería mejor recuperar algunos fragmentos de información más pequeños y manejables. Esto le devolverá el control a la aplicación más rápidamente y es un método para usar los recursos de la red desinteresadamente.

Ahora ya disponemos de datos, los podemos procesar usando mysql_fetch_row y moverse por el conjunto de datos usando mysql_data_seek, mysql_row_seek y mysql_row_tell. Veamos estas funciones:

mysql_pag_1

estudianteEsta pareja de funciones es más útil para moverse entre puntos conocidos en el conjunto de resultados. Tenga cuidado de no confundir el valor offset usado por row_tell y row_seek con el row_number usado por data_seek. Los resultados serían imprevisibles.

mysql_pag_2

Recuperación de datos

Bueno, ya estamos listos para escribir nuestra primera aplicación de recuperación de datos. Lo que haremos es recuperar todos los registros en los que la edad sea superior a cinco. Aún no sabemos cómo procesar los datos, de manera que lo único que vamos a hacer por el momento es recuperarlos. Llamaremos a esta aplicación select1.c.

 select1

Recuperar datos fila a fila

Para recuperar datos fila a fila que es lo que queremos hacer realmente, debe usar mysql_use_result en vez de mysql_store_result:

mysql_use_result

Igual que con la función mysql_store_result, mysql_use_result envía NULL si falla, y si tiene éxito envía un indicador de un objeto del conjunto de resultados. Sin embargo, se diferencia porque no ha recuperado ningún dato en el conjunto de resultados que ha iniciado.

estudianteDebe usar mysql_fetch_row repetidamente hasta que se hayan recuperado todos los datos con el fin de obtener los datos. Si no obtiene todos los datos de mysql_use_result, las operaciones posteriores que realice su programa para recuperar datos podrán devolver una información errónea.

¿Que pasaría si en vez de llamar a mysql_use_result llamáramos a mysql_store_result? Si usamos el primero contamos con muchos beneficios en lo referente a la gestión de los recursos, pero, no podremos usarlo con mysql_data_seek, mysql_row_seek, o mysql_row_tell, y la utilidad mysql_num_rows está limitada por el hecho de que realmente no comienza a funcionar hasta que no ha recuperado todos los datos.

 Sin embargo ninguno de estos aspectos disminuye de ningún modo los beneficios nombrados anteriormente: una carga de red con un mejor equilibrio y menos gastos de almacenaje para posibilitar grandes conjuntos de datos.

Modifiquemos el listado select1.c y hacemos los siguientes cambios llamando al programa select2.c:

select2

Observaremos que sigue sin poder obtener un recuento de filas hasta que se consiga el último resultado.

select2_salida

Sin embargo, si comprueba los errores frecuentemente y pronto, hará que podamos aplicarlo mucho más fácilmente el cambio a mysql_use_result. Esta codificación nos puede ahorrar dolores de cabeza en posteriores modificaciones de la aplicación.

Procesando los datos retornados

Bueno hemos aprendido a recuperar filas, ahora ya podemos procesar los datos realmente retornados. MySQL, al igual que otras muchas base de datos, proporciona dos clases de datos:

  • La información recuperada de la tabla, llamados datos de columna.
  • Los datos sobre los datos, comúnmente conocidos como meta datos, tal como nombres de las columnas y de sus tipos.

Veamos primero los datos propios dentro de un formulario utilizable. La función mysql_field_count proporciona información básica sobre el resultado de la consulta. Adopta su objeto de conexión y retorna el número de campos (columnas) en el conjunto de los resultados:

mysql_field_count

 Podemos usar de un modo más genérico mysql_field_count  para otras cosas, como averiguar porqué falló una llamada a mysql_store_result. Por ejemplo, si mysql_store_result envía NULL, pero mysql_field_count envía un número positivo, puede insinuar que se ha producido un error de recuperación. Sin embargo, si mysql_field_count retorna 0 significará que no existían columnas para recuperar, lo cual explicaría el fallo a la hora de almacenar el resultado. Es normal querer saber el número de columnas que, supuestamente, devolverá una consulta en particular. Sin embargo, esta función es más útil con componentes de consultas genéricos o en una situación en las que las consultas se construyan sobre la marcha.

estudianteEn el código escrito para las versiones más antiguas de MySQL  mysql_num_fields, que puede adoptar tanto una estructura de conexión como un indicador de estructura de resultados y enviar el número de columnas.

 Dejando a un lado los aspectos referentes al formato, ya sabe cómo mostrar los datos correctamente. Añadiremos una sencilla función display_row al programa  select2.c.

estudianteTenga en cuenta que ha hecho que la conexión, el resultado y la información de la fila enviada por mysql_fetch_row, sean globales para simplificar el ejemplo. Para la producción de código no se recomienda su uso.

Deja un comentario

Este sitio utiliza Akismet para reducir el spam. Conoce cómo se procesan los datos de tus comentarios.