30 ago 2012

INSERT y UPDATE condicionales en MySQL

En un proceso de importación de datos en el que deseemos fusionar la información existente con la que contiene nuestro fichero, tendremos la necesidad de actualizar o insertar según dicha información esté o no referenciada en la base de datos.
Supongamos que tenemos esta sencilla tabla en la que almacenamos títulos de libros, junto con una puntuación (que vendría a ser la suma de votos que da un usuario que accede al sitio)

idtitlepoints
1Alicia en el País de las Maravillas358
2El Color de la Magia324
3Hamlet321
Supongamos que el usuario escribe un título que envía al sistema. Si este libro se encuentra se incrementará e uno su puntuación (points). Si no es así se registrará el nuevo libro y se asignará el valor de puntuación a 1.

En principio suele abordarse este tipo de acciones con dos tareas:
  • Comprobar si existe el elemento
  • Ejecutar acción en base de datos en función de lo anterior

Es decir, la implementación exigiría realizar dos consultas consecutiva.El código para ilustrar como sería esta implementación (usando PHP y el framework de Joomla en este caso) podría ser:
    $db=new JFactory::getDBO();
    //comprobamos si existe el item
    $sql1='SELECT id,title,points
           FROM books
           WHERE title = "$titulo"';
    $db->setQuery($sql1);
    $resultado=$db->loadObjectList();

    //INSERT o UPDATE en función del resultado anterior
    if (empty($resultado)) {//libro no registrado; lo guardamos
        $sql2='INSERT INTO books 
              (title,points) VALUES 
              ("$titulo)","1");';
    }
    else {//el libro está; incrementamos su puntuación
        $sql2='UPDATE books SET points=points+1';
    }
    $db->setQuery($sql2);
    $db->query();


Tenemos que recurrir en este caso a PHP, como podría haber sido a otro lenguaje, para tomar una decisión de que consulta enviar al servidor de base de datos.
En este caso tenemos en mente MySQL.
Podremos resolver esto de otra forma más eficiente usando exclusivamente consultas, ahorrando así en líneas de código, consiguiendo una acción más portable a otros lenguajes y sobretodo ahorrando en tiempo de ejecución.
Hemos planteado el ejemplo con una única inserción, pero en el caso de tener que ejecutar miles de acciones de este tipo la diferencia de tiempo puede ser drástica (más si tenemos en cuenta el problema de tiempo de ejución máxima que tiene un script php en el servidor).
En primer lugar, puede que necesitemos permisos para modificar la estructura de la tabla con la que estamos trabajando, ya que necesitamos especificar que el título del libro va a ser un valor único, es decir, no permitimos el registro de varios libros con el mismo título.
Para ello ejecutamos la siguiente consulta (si es que el diseñador de la base de datos no lo ha tenido en cuenta...)

ALTER TABLE `books` ADD UNIQUE (`title`);

Tras esto podremos realizar operaciones de inserción o actualización automática en el caso de que el item esté, usando una única consulta con ON DUPLICATE KEY:

INSERT INTO `books` (`title`, `points`)
VALUES ('el color de la magia','1')
ON DUPLICATE KEY
UPDATE `points`=`points`+1

Como dato a tener en cuenta (al menos según el cotejamiento de la BD) en el caso del ejemplo si otro usuario pretende insertar 'EL COLOR DE LA MAGIA' se activará ON DUPLICATE KEY, incrementándose el valor points en lugar de insertarse otro nuevo registro.
No se toman en cuenta las mayúsculas y minúsculas, lo cual en este caso no nos viene del todo mal, pero sí que podría ser necesario diferenciar en otros.
De cualquier forma, normalizar previamente el dato a tratar con la base de datos nos ayudará a tener un mayor control en este aspecto.

No hay comentarios:

Publicar un comentario