miércoles, 1 de mayo de 2013

Integrida Referencial en Sqlite


Integrida Referencial en Sqlite

SQLite es un pequeño ‘sistema’ de base de datos: es una biblioteca que sobre un archivo almacena el esquema de la base de datos y los datos propiamente tal. En términos generales presenta un buen rendimiento, y está aconsejada para quienes requieren almacenar un volumen medio de datos en sus aplicaciones, utilizando un esquema relacional.
Dentro de lo que podemos llamar una desventaja de SQLite es que no implementa la integridad referencial, es decir, no disponemos de claves foráneas (puedes declararlas, pero el intérprete ignorará tu declaración). De todas formas, SQLite si implementa triggers, así que hay algunas formas de implementar la integridad referencial de esa forma.
¿Un ejemplo? Pero con mucho gusto… de todas maneras, el ejemplo está pensado solo para mostrar las características de SQLite, así que no esperes un buen diseño ni nada por el estilo…
Por ejemplo, imagínese una pequeña empresa de buses que requiere almacenar información acerca de sus viajes. Para ello, supongamos que tenemos la tabla buses (con información de los buses), viaje (con los datos de un viaje) y pasajero (con los datos de un pasajero). Una propuesta entonces podría ser la siguiente:
1
2
3
4
5
6
7
8
9
10
11
12
13
create table buses(
  id integer primary key not null autoincrement,
  matricula text not null,
  ano integer not null,
  capacidad integer not null);
 
create table pasajero(
  cedula text primary key not null,
  nacimiento date not null);
 
create table viaje(
  id_bus integer not null,
  cedula text not null);
Bueno, hay algunas cosas por explicar de acá:
  • SQLite implementa el incremento automático de un campo (autoincrement), el que perfectamente pude indicarse para el primary key.
  • Las claves foráneas, como ya mencioné, no están implementadas aún en SQLite, así que no las agrego.
  • La primary key de la tabla viaje será su OID.
SQLite, para almacenar una tupla en una tabla, utiliza el sistema de OID, es decir, asigna secretamente un identificador para cada tupla, que facilita su búsqueda en el arbol B (la estructura de datos usada por SQLite para almacenar los datos en archivo). No necesito declarar el OID, pero puedo consultarlo en cualquier tabla; pronto un ejemplo de eso.
Ahora, insertemos algunos datos, para hacer las pruebas.
1
2
3
4
5
6
insert into buses values(null, 'ABCD-01', 2008, 40);
insert into buses values(null, 'JANO-05', 2007, 27);
insert into buses values(null, 'INSQ-35', 2007, 20);
insert into pasajero values('123ASD-K', 1980-05-23);
insert into pasajero values('237QSD-3', 1970-03-28);
insert into pasajero values('135QTW-3', 1989-08-05);
Con esos datos, podemos ver en acción el sistema de los OID:
sqlite>select oid,* from buses;
1|1|ABCD-01|2008|40
2|2|JANO-05|2007|27
3|3|INSQ-35|2007|20
sqlite>select oid,* from pasajero;
1|123ASD-K|1980-05-23
2|135QTW-3|1989-08-05
3|237QSD-3|1970-03-28
Puede parecer extraño lo que ocurre con la tabla buses, puesto que ya existe su primary key, y esta se autoincrementa. Bien, pues de todas formas se genera un OID… pero cuando se define una primary key autoincrementable de tipo integer, el OID tendrá el mismo valor que dicha clave. No ocurre lo mismo en la tabla pasajero, puesto que su primary key es un texto, y no un entero. Nota también que en los campos que se autoincrementan, el siguiente valor se genera insertando un null.
Gracias a esta característica es que he definido la tabla viajes sin primary key: confío en el OID (lo que no es recomendado, por cierto).
Ahora, la parte artística: ¿y las claves foraneas? Como mencioné, para eso disponemos de los triggers…
1
2
3
4
5
6
7
8
9
10
create trigger fki_viaje_buses
before insert on viaje
for each row
begin
  select raise
     (rollback,
     'Inserción en viaje viola restricción fki_viaje_buses')
  where
     (select id from buses where id=new.id_bus) is null;
end;
Luego de eso, intentemos insertar un viaje para un bus no válido:
sqlite> insert into viaje values(5, '123ASD-K');
SQL error: Inserción en viaje viola restricción fki_viaje_buses
El trigger para la actualización (update) es exactamente igual, pero en el caso de la eliminación tenemos un problema: si quiero eliminar un bus ¿qué hago con los viajes? Por como se definió la base de datos, no se pueden eliminar buses o pasajeros mientras hayan viajes, así que manos al trigger:
1
2
3
4
5
6
7
8
9
create trigger fkd_viaje buses
before delete on buses
for each row
  begin
    select raise(rollback, 'No se puede eliminar bus')
    where
       (select id_bus from viaje where old.id = id_bus)
    is not null;
end;
Ahora, si yo he insertado los siguientes datos de viaje
1
2
insert into viaje values(1, '135QTW-3');
insert into viaje values(1, '123ASD-K');
el trigger impedirá que eliminemos un bus, lo que generaría un estado inconsistente en la base de datos
sqlite> delete from buses where id = 1;
SQL Error: No se puede eliminar bus
Como se puede apreciar, SQLite es una alternativa interesante cuando se necesita un acceso a datos rápido y sencillo. Aunque me parece que SQLite es muy bueno, no me atrevería aún a usarlo en un entorno más de producción… aunque en la medida que lo voy usando, más me convence.

1 comentario:

  1. Buena Alejandro....gracias por todos los aportes...nos ayudan mucho de verdad!!!!.
    Bueno quería quitarte un poquito de tu tiempo para que puedas orientarme acerca de 2 dudas en particular, espero no sea mucha molestia!....
    La primera: quiero que en mi app en el apartado de opciones se pueda cambiar el color de fondo, como añadir una paleta con ciertos colores y que el usuario pueda elegir el que mas guste.
    Segundo: la app es sencilla pero quiero sacarla en version libre y completa....vi en algunas apps igual en el apartado de opciones que tienen ID y al lado un editText para introducir un código y la app recien se torne a version completa.....esto lo hago mediante base de datos?.....por favooooooooorrrr una iluminada!!!.....de antemano se te agradece mil!!!.....sigue así, valioso esfuerzo para introducirnos en este mundo de programación android!!!....

    ResponderEliminar