Notxor tiene un blog

Defenestrando la vida


Cómo trabajar con SQLite3 desde Emacs

En mi último artículo hablé de cómo trabajo en formato texto con Emacs y son varios los que me han preguntado por el apartado de base de datos. ¿Cómo me las apaño para tener las bases de datos de SQLite en modo texto?

Lo primero es confesar que no tengo todas, sino sólamente aquellas que quiero tener controladas en repositorios git. La otras las guardo en el fichero de base de datos que genera SQLite, porque es más sencillo y rápido acceder a los datos así. Pero aunque no las tenga en formato de texto, sí hay consultas, en mi fichero de consultas, que las referencias.

Para explicar mejor el proceso que sigo, voy a contar desde cero y pondré también capturas de pantalla, paso a paso.

Crear una base de datos

Lo primero que hago es crear un fichero con el nombre que quiero ─en el ejemplo lo llamaré base-de-datos, por ejemplo─ y le añado una extensión sql. Al abrir este fichero, Emacs entrará en modo SQL, y por defecto se colocará en modo ANSI.

base-datos-vacia.png

Como se puede ver en la imagen, la barra de estado marca el modo SQL[ANSI], aunque el archivo aún está vacío. Lo primero es decirle al editor que vamos a utilizar, concretamente SQLite. Para ello utilizo el comando M-x sql-set-product RET sqlite RET.

Estando en el buffer del fichero sql, que permanece vacío, vamos a levantar también un entorno interactivo para SQLite dentro de Emacs: así no tenemos que estar entrando y saliendo del editor, sino sólo cambiando de buffer. Para lanzarlo utilizo la combinación de teclas C-c TAB también se podría llamar mediante M-x sql-product-interactive. Al lanzarlo nos pide un nombre para la base de datos, en mi ejemplo base-de-datos.db y nos mostrará el típico prompt de SQLite.

sqlite-lanzado.png

Ahora podemos dar el siguiente paso en cualquier de los dos buffers pero para mí resulta mucho más sencillo hacerlo directamente en el interactivo de SQLite. Vamos a crear una tabla dentro de nuestra base de datos.

CREATE TABLE Gente (
    id       integer primary key autoincrement,
    Nombre   text,
    Telefono text);

Y para que no esté vacía la base de datos, voy a añadir un par de registros a esa tabla:

insert into Gente(Nombre, Telefono) Values('Juan', '987654321');
insert into Gente(Nombre, Telefono) Values('Juan', '987654321');

Bien, vamos a hacer una consulta y como me gusta ver las cosas ordenadas, también añado los comandos .mode column y .headers on para que se muestren las consultas, como por ejemplo:

SELECT * FROM Gente;
id          Nombre      Telefono  
----------  ----------  ----------
1           Pepe        123456789 
2           Juan        987654321

Como vemos, hasta ahora hemos trabajado como si lo hiciéramos desde la herramienta interactiva de SQLite3, y además tenemos dos ficheros, la base de datos db con una tabla y dos registros y un fichero sql vacío:

crear-base-datos.png

Convertir la base de datos a texto

Cuando tenemos la base de datos creada la podemos pasar a formato texto desde el mismo buffer de SQLite con dos sencillos comandos:

.output base-de-datos.sql
.dump

Esos dos comandos, primero lo que hacen es dirigir la salida de SQLite al fichero base-de-datos.el y luego volcar toda la base de datos (también soporta hacerlo por tablas). Si ahora recargamos el fichero de texto, veremos lo siguiente:

volcado-texto.png

Ahora, podemos borrar el fichero binario de base de datos y trabajar sólo con el sql. Por ejemplo, me he dejado un par de detalles que en formato de texto se pueden modificar sobre la marcha. En nuestra base de datos no tendría sentido tener teléfonos sin nombre, o nombres sin teléfonos, así que debería haber marcado esos campos como NOT NULL. Así, he modificado el fichero de texto para dejarlo como sigue:

PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE Gente (
  id       integer primary key autoincrement,
  Nombre   text    not null,
  Telefono text    not null);
INSERT INTO Gente VALUES(1,'Pepe','123456789');
INSERT INTO Gente VALUES(2,'Juan','987654321');
DELETE FROM sqlite_sequence;
INSERT INTO sqlite_sequence VALUES('Gente',2);
COMMIT;

Si borramos la base de datos binaria la podremos recargar desde ese fichero de texto, generado antes. Vamos a hacer la prueba para demostrar lo sencillo que es. Volvemos a lanzar el entorno interactivo desde el buffer de nuestro código sql con C-c TAB y el nombre del fichero, que podemos utilizar, que puede ser el mismo u otro. Una vez hecho, si pedimos que nos muestre el .schema de la base de datos nos dirá que está vacía, pero si desde nuestra base de datos en sql le lanzamos el buffer con C-c C-b y volvemos a preguntar, ya veremos nuestra estructura cargada.

Así mismo, si por algún casual una base de datos va a gestionarse desde un entorno de base de datos, como puede ser MariaDB o PostgreSQL o el cualquier otro que comprenda el sql, bastaría con modificar mínimamente ese fichero para ajustarse a las idiosincrasias de cada base de datos y convertirla con facilidad. Por eso es recomendable que utilicemos siempre código sql lo más estándar posible para facilitar luego la exportación de datos.

Fichero para consultas

Ya he explicado un poco por encima cómo funciona el Emacs en modo sql. En este caso sobre SQLite3, pero se puede conectar a un buen puñado de bases de datos relacionales.

Además suelo tener en «el cargador» otro fichero que guarda código para hacer determinadas consultas. La mayoría de esas consultas se hacen sin cargar el entorno interactivo sin abrir el fichero sql y es lo que mostré en el artículo anterior, por lo que no me extenderé en ello demasiado. Por ejemplo:

#+begin_src elisp :export code
(require 'sqlite)
(let ((db (sqlite-init "~/proyectos/tutorial-sqlite/base-de-datos.db")))
  (sqlite-query db ".headers on")
  (setq resultado (sqlite-query db "select * from Gente"))
  (sqlite-bye db)
  resultado)
#+end_src

#+RESULTS:
| id | Nombre | Telefono   |
|  1 | Pepe   | 123456789  |
|  2 | Juan   | 987654321  |

Guardarlo así me permite generar tablas complejas y que son devueltas en un formato muy próximo al que puede gestionar org-mode. En el ejemplo, puesto que se tratan de datos muy básicos y no hay datos cruzados entre diferentes tablas, puede parecer innecesario. Sin embargo, cuando la base de datos se complica y/o las consultas implican varios join o necesitas trabajar los resultados de otra forma, como llevarlos a un informe, por ejemplo; el tener ese fichero de consultas es un ahorro de tiempo y quebraderos de cabeza. El código de consulta sql lo tienes a mano, lo puedes modificar para ajustarlo a la consulta que necesitas en ese momento y los resultados los tienes inmediatamente al pulsar C-c C-c en el bloque de código.


Comentarios