miércoles, 30 de abril de 2014

Indices únicos de campos de gran tamaño

Cuando queremos que una clave no esté duplicada entre las filas de alguna de nuestras tablas, es sencillo hacerlo, creamos una clave única (unique key) y nuestro gestor de bbdd tendrá cuidado de que no ocurra la duplicidad.


Pero que ocurre cuando la clave que queremos que sea única, mide más de 3.000 caracteres. Pues tenemos un problema porque en Postgresql, el máximo permitido son 2.000 y pico.


Se nos podría ocurrir, dividir el campo en dos campos y montar el índice sobre dichos campos. Pero obtendremos el mismo error.


La solución sería hacer un índice calculado, que fuese el hash del campo. El problema es que al ser un campo de más de 3.000 puede que tengamos colisiones indeseadas o falsos positivos.


CREATE UNIQUE INDEX uk_tabla
  ON esquema.tabla
  USING btree
  (
   md5(campo_unico)
  );


Para tratar de minorar el problema, podemos hacer un índice con un hash de parte de la clave (caracteres del 1-1500), compuesto por otro hash, con la parte restante de la clave (caracteres del 1501 al 3000). Así podríamos dividir cuantas veces queramos la clave, segmentando el índice, para hacer más difícil la colisión del índice.




CREATE UNIQUE INDEX uk_tabla_campo_unico
  ON esquema.tabla
  USING btree
  (
   md5(substr(campo_unico::text, 1, 1500)),
   md5(substr(campo_unico::text, 1501, 3000))
  );




Y en mysql. En este gestor tenemos un problema, no podemos aplicar la solución directamente, porque no existen los "índices calculados".


Por tanto la única solución para simularlos, es crear los campos calculados en la propia tabla. Y serán rellenados a través de triggers.


alter table tabla add trozo1 varchar(1500);
alter table tabla add trozo2 varchar(1500);


DELIMITER //
CREATE TRIGGER `tabla_insert` BEFORE INSERT ON `tabla` FOR EACH ROW BEGIN
 set NEW.trozo1  = sha1( SUBSTRING(new.campo_unico,1,1500));
 set NEW.trozo2 = sha1( SUBSTRING(new.campo_unico,1501));
end//
DELIMITER ;


CREATE UNIQUE INDEX uk_tabla_campo_unico
ON tabla
( trozo1, trozo2 );


Resumiendo si tenemos el problema de indexar de forma única un campo excesivamente largo, en Postgres tenemos la inestimable ayuda de la potencia de los índices calculados.


Por contra en Mysql tendremos que cambiar la estructura de la tabla y crear triggers que mantenga la información actualizada, y por último crear el índice, lo cual es bastante más incómodo a la vez que tedioso en la programación.













No hay comentarios: