SQLite3. Todos tus datos en tu bolsillo
PROGRAMACION
SQLite3. Todos tus datos en tu bolsillo
2016-01-12
Por
Horas Cle Sequel

No todos necesitamos Oracle... la mayoría tampoco necesitamos mysql para la mayoría de nuestros programas, ni poder acceder a nuestros datos a través de la red. La mayoría de las veces un simple array de estructuras y un fichero de texto nos soluciona el problema... pero a veces... necesitamos solo un poquito más.
Sqllite3 es una base de datos SQL súper ligera. Hoy por hoy la podéis encontrar en todos los teléfonos y tablets que corren Android y ha estado disponible en los sistemas GNU/Linux durante ya bastante tiempo. En esta artículo os vamos a contar como utilizarla de forma muy sencilla y rápida en vuestros programas .

Para poder preparar un ejemplo funcional (uséase que funciona), lo primero que necesitamos es una base de datos. Bueno, lo primero es realmente instalar sqlite, incluyendo los paquetes de desarrollo. Pero eso ya lo sabíais verdad? ;)

Bueno vamos a reducir nuestro ejemplo al mínimo. La típica aplicación de biblioteca con una única tabla para los libros. Luego vosotros podréis añadir las tablas que falten y convertir nuestras 10 o 20 líneas de código en una aplicación de verdad.

CREANDO UNA BASE DE DATOS

Crear la base de datos es una tarea muy sencilla que se puede hacer de forma interactiva. En nuestra línea de comandos escribimos:

$ sqlite3 biblio.db
  

De esta forma creamos un fichero llamado biblio.db que contendrá nuestros datos, nos conectamos a esa base de datos e iniciamos el intérprete interactivo de sqlite. Ahora tendríais que conocer DDL (Data Definition Language o Lenguaje de Definición de datos), el lenguaje que cada base de datos usa para definir los datos que va a contener.

A diferencia de los lenguajes de consulta, los DDLs varían ligeramente de base de datos a base de datos, aunque son todos muy parecidos. En esta página (http://www.sqlite.org/lang_createtable.html) podréis encontrar todos los detalles de como hacer esto.

En el principio de los tiempos había una diferencia entre lenguajes de definición de datos y lenguajes de consultas. Posteriormente SQL se impuse y DDL pasó a ser un subconjunto de éste

Bueno, nosotros vamos a crear una tabla llamada libros en la que almacenaremos un código interno, el título del libro, la fecha en la que se adquirió y cuantos ejemplares tenemos... Sí esto es un poco arbitrario, pero de esta forma podemos trabajar con los principales tipos de datos (números, cadenas y fechas). Hay un montón más, pero estos son los que mas vais a utilizar.

DDL (Data Definition Language) es el lenguaje utilizado para definir los datos en nuestra BD
Así nuestra tabla se definiría de la siguiente forma:

sqlite> create table libros (
   ...> id INTEGER PRIMARY KEY,
   ...> titulo TEXT,
   ...> fecha DATE,
   ...> disponibles INTEGER);
sqlite> 

Podemos comprobar muy fácilmente la estructura de nuestra base de datos utilizando el comando .schema que nos mostrará, exactamente lo que acabamos de escribir un poco más arriba.

ALGUNOS DATOS

Para que nuestro programa genera alguna salida, necesitamos que la base de datos contenga algún dato... que sino.. de qué?. Para añadir datos a nuestra base de datos utilizaremos el comando INSERT. Así vamos a almacenar la información de un par de libros

sqlite> insert into libros (titulo, fecha, disponibles) 
   ...> values ("1984", datetime("now"), 1);
sqlite> insert into libros (titulo, fecha, disponibles) 
   ...> values ("2013", datetime("now"), 2);

Para acceder a los datos que acabamos de introducir podemos ejecutar la siguiente consulta:

sqlite> select * from libros;
1|1984|2013-07-12 13:46:13|1
2|2013|2013-07-12 13:46:19|2

Si, datetime es una función que nos permite convertir cadenas de caracteres en fechas.

ACCEDIENDO A LOS DATOS DESDE C

Por ahora todo muy bonito, pero lo que realmente nos interesa es poder usar la base de datos desde nuestro programa. Y a ello vamos. No nos seáis impacientes.

Al código de este artículo le hemos dado bastantes vueltas y al final hemos decidido mantener el ejemplo en la página de Sqlite3, simplemente cambiando de lugar algunas cosas para que la explicación que sigue sea más directa. Es sencillo y muy ilustrativo, así que ahí va:

#include <stdio.h>
#include <sqlite3.h>

static int callback (void *NotUsed, int argc, char **argv, 
       	             char **azColName);

int main(int argc, char **argv){
    sqlite3 *db;
    char *zErrMsg = 0;
    int rc;

    rc = sqlite3_open(argv[1], &db);
    if( rc ){
      fprintf(stderr, "Can't open database: %s\n", 
                      sqlite3_errmsg(db));
      sqlite3_close(db);
      return(1);
   
    rc = sqlite3_exec(db, argv[2], callback, 0, &zErrMsg);
    if( rc!=SQLITE_OK ){
      fprintf(stderr, "SQL error: %s\n", zErrMsg);
      sqlite3_free(zErrMsg);
    }
    sqlite3_close(db);
    return 0;
  }
  static int callback(void *NotUsed, int argc, char **argv, 
  	              char **azColName){
    int i;
    for(i=0; i<argc; i++){
      printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
    }
    printf("\n");
    return 0;
  }


Ahora solo tenemos que compilar el programa con una línea como esta (no olvidéis añadir la librería sqlite3):

$ gcc -o list list.c -lsqlite3

Para ejecutarlo, debemos pasarle como parámetros el fichero que conteniene nuestra base de datos, seguido de la consulta que queremos realizar. Por ejemplo:

$ ./list biblio.db "select * from libros;"

No os olvidéis de las comillas y del punto y coma al final del comando.

LA EXPLICASAO

Bueno, aunque seguro que la mayoría de vosotros sabéis perfectamente lo que hace el programa, vamos a incluir una breve explicación para los que estáis empezando.

El tipo sqlite3* representa la la base de datos en nuestro programa
Tras los ficheros de cabecera de rigor y la declaración del prototipo de una función que definiremos más abajo, nos encontramos el programa principal. Todas las funciones para acceder a la base de datos necesitan un parámetro del tipo sqlite3*. Ese es el manejador de nuestra base de datos.

Así que lo primero que nos encontramos es una llamada a la función sqlite3_open que, como su propio nombre indica, abre (inicializa) la base datos contenida en el fichero que le pasamos como primer parámetro, y da un valor a nuestro manejador (la variable del tipo sqlite3* de la que hablábamos en el párrafo anterior).

En caso de error, preguntamos a la base de datos cual es el problema (sqlite3_errmsg), cerramos la base de datos y nos piramos.

La Consulta

Si todo ha ido bien, ya podemos comenzar a usar la base de datos. En el caso más sencillo, el comando sqlite3_exec nos permite ejecutar cualquier comando que podamos ejecutar en el interprete interactivo. Podemos insertar datos, actualizarlos, listarlos o borrarlos, simplemente pasando como segundo parámetro el comando SQL apropiado. Veremos unos ejemplos en un momento.

La función recibe tres parámetros más (además del manejador de base de datos que tenemos que utilizar siempre). El primero (que es realmente el tercer parámetro) es un puntero a la función que queremos que se ejecute cada vez que la base de datos nos devuelva un valor. Esto ocurre normalmente en las consultas, los comandos SQL SELECT en los que, típicamente, obtendremos varios resultados

El siguiente parámetro es un puntero void que se pasará a la función que acabamos de especificar como primer parámetro. Por ejemplo, si queremos almacenar en un array los resultados de nuestra consulta para hacer algún cálculo sobre ellos, ese array lo podríamos pasar a la función de callback de esta forma. En este ejemplo no se utiliza y lo ponemos a 0.

El último parámetro es una cadena que, en caso de error, nos informará de lo que ha ocurrido. Esa cadena es la misma que obtendrías utilizando vuestra consulta en el interfaz de la línea de comandos. Observad que al función sqlite3_exec reserva memoria para esa variable (solo si se produce un error), y que nosotros tenemos que liberarla cuando ya no necesitemos esa información.

Tras ejecutar la consulta, cerramos la base de datos, y aquí paz y después gloria.

LA FUNCIÓN DE CALLBACK

Para terminar con el código vamos a analizar la función de callback (o de llamada de vuelta si se me permite la traducción), la función que realmente recibe los datos de la base de datos. Esta función recibe cuatro parámetros.

El primero de ellos es un puntero. Es exactamente el puntero que hayamos pasado como cuarto parámetro a la función sqlite3_exec. En este caso no lo estamos utilizando para nada, pero en un programa real será la forma que tengáis de agregar los datos de vuestras consultas a la base de datos.

El siguiente parámetro que nos encontramos es el número de campos en la línea que estamos procesando. Por ejemplo, para la consulta:

select id, titulo from libros;

El parámetro argc siempre valdrá 2, ya que estamos pidiendo solo dos campos: id y titulo. Si en lugar de esos dos campos, utilizamos un asterisco, select nos devolverá tantos campos como hayan sido definidos en la tabla.

Dicho esto, ya os podéis imaginar que contiene el siguiente parámetro. Efectivamente, los valores de los campos que hayamos indicado para el resultado concreto que estamos recibiendo. El último parámetro es un array de cadenas de caracteres conteniendo los nombres de los campos que vamos a recibir.

Con todo esto, la función de callback simplemente muestra, para cada línea que devuelve select (que en el argós de las bases de datos se llama tupla), muestra los resultados precedidos por el nombre del campo que le hayamos dado al definir nuestra base de datos.

PROBANDO, PROBANDO

Es hora de probar. Como ya habíamos metido algunos datos en nuestra base de datos, podemos empezar con un comando como este:

$ ./list biblio.db 'select * from libros;'

Ahora podemos introducir un nuevo libro en nuestra base de datos:

./list biblio.db 'insert into libros (titulo, fecha, disponibles) values ("El Juego de Ender", datetime("now"), 3);'

Si ahora volvemos a listar todos los libros (usando, el primer comando), veremos una nueva entra con los datos que acabamos de introducir. También podemos comprobar cuantos libros tenemos en nuestra biblioteca.

./list biblio.db "select sum(disponibles) from libros;"

El límite es el select :P

Hasta la próxima.

Library by Foundry: Public Domain


SOBRE Horas Cle Sequel
Horas es un experto en bases de datos, no hay query que se le resista ni modelo de datos demasiado complejo para él. En su tiempo libre colabora con la ONG un gato, una sonrisa, un tweet, con la que hace felices a medio mundo y entretiene a la otra mitad.

 
Tu publicidad aquí :)