Miniacertijo en MySQL.

Esta mañana he estado un rato alucinando con unos resultados que me estaba dando el servidor de MySQL ante unas instrucciones de lo más simples cuyos resultados esperados eran a priori evidentes. Es probable que estuviera bajo los influjos de demasiado café, quién sabe, pero el caso es que durante unos buenos minutos no podía dar crédito a lo que estaba viendo. He aquí la secuencia de consultas llevadas a cabo:




mysql> select count(*) from t;
+----------+
| count(*) |
+----------+
|       73 |
+----------+
1 row in set (0.00 sec)

mysql> update t set f = null;
Query OK, 73 rows affected (0.00 sec)
Rows matched: 73  Changed: 73  Warnings: 0

mysql> select count(*) from t where not isnull(f);
+----------+
| count(*) |
+----------+
|        4 |
+----------+
1 row in set (0.00 sec)

Ahí al final, por supuesto, esperaba recibir un 0. Pero no...

He llegado a pensar que MySQL se había vuelto loco, que el servidor había cascado... qué se yo, tonterías. Al final he descubierto qué estaba pasando. ¿Alguien se anima a especular? De hecho con el nivelillo de algunos de los que a veces comentáis, estoy seguro que mientras leíais este post ya se os ha ocurrido qué estaba pasando, así que más que la solución, os propongo que comentéis cuánto rato os ha llevado dar con ella.

Resolución.

Pues el ganador por aproximación ha sido Pedro Cambra que ha comentado "que la tabla tenga algún tipo de clave foránea, un trigger o mucha actividad y según hayas hecho el update se hayan modificado datos". Efectivamente, en su día creé dos triggers sobre la tabla en cuestión, uno sobre inserciones (irrelevante en este caso) y otro para actualizaciones. Los dos hacían lo mismo: si al insertar o modificar un registro el campo g tenía un valor concreto 'x', el campo f tomaría otro valor concreto 'y' independientemente del que el usuario hubiera introducido. Esto estaba haciendo ahora que para 4 registros concretos (los que cumplián la condición para el campo g) el trigger impidiera que f tomara valor NULL para pasar a tomar el valor que en el trigger estaba previsto que tomara. Pero claro, había olvidado que esta tabla tenía estos triggers y de ahí vinieron esos minutos de incredulidad...

Tags:

22 Responses to “Miniacertijo en MySQL.”

  1. Luis Medel se atrevió a comentar:

    Pues tío, lo veo y no lo creo. Ahora mismo no tengo un MySql delante pero juraría que he leído por algún sitio que en ocasiones MySql devuelve resultados referentes a las últimas filas añadidas a la tabla por temas de compatibilidad con versiones anteriores (creo que era algo de eso)

    Pero vamos, no tengo ni idea.

    Otra cosa que se me ocurre es que el campo sea NOT NULL (y no te sirva de nada el update) pero contenga algunos registros con 0 o con la cadena vacía que, curiosamente, se suelen interpretar como NULL en algunos motores de BBDD y de ahí el resultado.

    Vamos, que por elucubrar que no quede, pero no tengo ni idea :)

  2. pedro se atrevió a comentar:

    Uf, así a bote pronto mi apuesta iría al tipo de campo que actualizas a null en combinación con el sql_mode por defecto que genera valores "zero" por defecto ¿podría ser un campo de tipo fecha?

    Muy interesante el acertijo :-D en algunos blogs de mysql llevan un mysql quiz con preguntas de este tipo :-)

  3. Jordi Bufí se atrevió a comentar:

    Son las 00.30 de San Juan y acabo de ver "El curioso caso de Benjamin Button" ¿eso sirve como excusa para no dar con la solución? Además hace mucho que no toco SQL así que seguramente digo una chorrada... pero puede ser que tengas diferentes tipos de campos y alguno de ellos no puede ponerse a null?

    Sería una explicación si no fuera porque el update te da el mismo número que el count... no sé, hay campos "mixtos"? En fin, mejor me voy a dormir xD

  4. Albert Mata se atrevió a comentar:

    Vale. Releyéndome con ojos de "no sé la solución" realmente no es nada evidente. Pero el campo f es de tipo SMALLINT(5) UNSIGNED y admite NULL. Como pista 1, no he incluído la sentencia de creación de la tabla t porque la creé hace meses -con lo que ello implica-. Y como pista 2, la tabla t incluye otros campos, por ejemplo uno al que podemos llamar g que en este caso es de tipo TINYINT(1) UNSIGNED NOT NULL y que ejerce de booleano.

  5. Luis Medel se atrevió a comentar:

    Macho, porque me tengo que ir a la playa ahora. Pero esto no se queda así... :P

  6. Albert Mata se atrevió a comentar:

    Para evitar pensamientos extraños, cuando la tabla se creó hace meses la sentencia de creación podría perfectamente haber sido algo tan simple como...

    CREATE TABLE t (
    id SMALLINT(5) UNSIGNED AUTO_INCREMENT,
    g TINYINT(1) UNSIGNED NOT NULL,
    f SMALLINT(5) UNSIGNED,
    PRIMARY KEY(id)
    );

    ...vamos, que "la trampa" no está en la creación de la tabla.

  7. Luis Medel se atrevió a comentar:

    Pues ya está. El update te setea el fampo "f" a 0 en vez de a NULL...

  8. Albert Mata se atrevió a comentar:

    Nops. Si así fuera, en lugar de devolver 4 devolvería 73. Además, ¿por qué debería hacerlo si f admite NULL?

  9. Luis Medel se atrevió a comentar:

    Pues ahí llevas mi último cartucho (se nota que tengo ganas de saber qué te está pasando, no?)...

    Acabo de darme cuenta de que he pasado por alto lo del isnull() ¿Seguro que hace lo que crees que hace? Yo generalmente uso "IS NULL" para este tipo de comprobaciones. La función isnull() la uso (en SQLServer) para obtener un valor por defecto en caso de que el campo sea NULL. Es decir, el equivalente a la función nvl() de Oracle.

    Y aquí me quedo.

  10. Albert Mata se atrevió a comentar:

    Nada nada, el ISNULL está bien utilizado ahí. De hecho si hago un SELECT compruebo que, efectivamente, todos los registros excepto 4 están a NULL, pero esos 4 registros "no se han visto afectados" por el UPDATE.

  11. pedro se atrevió a comentar:

    mmm, creo que ya lo he visto (ahora me hago el misterioso como tú ;) )

  12. Albert Mata se atrevió a comentar:

    Pues te quedarás sin el apartamento en Alicante...

  13. Pote se atrevió a comentar:

    LA respuesta es por el query_cache de mysql

  14. Albert Mata se atrevió a comentar:

    @Pote. Nops, no van por ahí los tiros.

    ¡Ups, dije tiros!

  15. pedro se atrevió a comentar:

    Estaba vacilando, creo que perderé el apartamento en Torrevieja.

    Te digo lo que descarto, no es el sql_mode ya que el campo está definido como NOT NULL y entonces no hay valores zero que valgan.
    Tampoco puede ser un problema con el UPDATE porque no te muestra warnings.
    No puede ser además problemas transaccionales con tablas innodb porque estás, en principio en la misma sesión, aunque no hayas hecho commit, tienes que ver los cambios...

    Me quedo con dos opciones locas, una, que el campo g tenga 4 valores diferentes y de alguna manera el count(*) esté contando eso (no lo creo la verdad).
    La segunda es que la tabla tenga algún tipo de clave foránea, un trigger o mucha actividad y según hayas hecho el update se hayan modificado datos.

    Sácanos de las dudas!

  16. pedro se atrevió a comentar:

    Corrijo: Quería decir que el campo está definido para que admita NULL y por eso no hay valores "zero" por defecto, independientemente del sql_mode

  17. Albert Mata se atrevió a comentar:

    Post actualizado con la resolución al miniacertijo. El apartamento en Torrevieja se lo lleva finalmente Pedro (mi secretaria se pondrá en contacto contigo, descuida).

  18. Luis Medel se atrevió a comentar:

    Vaya, yo pensaba que partíamos de la base de que nada más estaba tocando la tabla y que eran "cosas raras" de esas que pasan de vez en cuando...

    TONGO, TONGO!!!!!! :p

  19. Albert Mata se atrevió a comentar:

    Nonononono... no se dijo en ningún momento... :-p Por cierto, el comentario quedó en aprobación porque en el mail pusiste .cmo en lugar de .com... no es que tenga recensurado o algo así... :-p

  20. Jordi Bufí se atrevió a comentar:

    No debería haber comentado xD

  21. pedro se atrevió a comentar:

    Aha! :-D ya estoy esperando las llaves ;) o eres como los 905 esos que luego no dan el premio XD

  22. Albert Mata se atrevió a comentar:

    Leches! Acertaste de nuevo! Estás en racha...

Leave a Reply