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.
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: MySQL
Junio 23rd, 2009 at 18:24
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
Junio 23rd, 2009 at 23:28
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
en algunos blogs de mysql llevan un mysql quiz con preguntas de este tipo
Junio 24th, 2009 at 00:39
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
Junio 24th, 2009 at 09:21
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.
Junio 24th, 2009 at 10:16
Macho, porque me tengo que ir a la playa ahora. Pero esto no se queda así...
Junio 24th, 2009 at 10:28
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...
...vamos, que "la trampa" no está en la creación de la tabla.
Junio 24th, 2009 at 19:46
Pues ya está. El update te setea el fampo "f" a 0 en vez de a NULL...
Junio 24th, 2009 at 20:01
Nops. Si así fuera, en lugar de devolver 4 devolvería 73. Además, ¿por qué debería hacerlo si f admite NULL?
Junio 24th, 2009 at 20:42
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.
Junio 25th, 2009 at 08:20
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.
Junio 25th, 2009 at 21:09
mmm, creo que ya lo he visto (ahora me hago el misterioso como tú
)
Junio 25th, 2009 at 21:15
Pues te quedarás sin el apartamento en Alicante...
Junio 26th, 2009 at 15:47
LA respuesta es por el query_cache de mysql
Junio 26th, 2009 at 15:50
@Pote. Nops, no van por ahí los tiros.
¡Ups, dije tiros!
Junio 26th, 2009 at 21:19
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!
Junio 26th, 2009 at 21:21
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
Junio 29th, 2009 at 06:24
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).
Junio 29th, 2009 at 07:48
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
Junio 29th, 2009 at 07:53
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
Junio 29th, 2009 at 15:57
No debería haber comentado xD
Julio 2nd, 2009 at 15:43
Aha!
ya estoy esperando las llaves
o eres como los 905 esos que luego no dan el premio XD
Julio 2nd, 2009 at 15:52
Leches! Acertaste de nuevo! Estás en racha...