Recientemente perdí más de media hora intentando descubrir porque MySQL me devolvía un resultado a priori carente de sentido al efectuar una simple diferencia entre dos números enteros. Para reproducir el problema utilizaré una tabla simple reducida únicamente a los campos imprescindibles para mostrar el problema. Creo pues la tabla stk_stockmonth con esta estructura:
| Field | Type | Null | Key | Default | Extra |
+---------+----------------------+------+-----+---------+-------+
| stk_ini | smallint(5) unsigned | YES | | NULL | |
| stk_inc | smallint(5) unsigned | YES | | NULL | |
| stk_out | smallint(5) unsigned | YES | | NULL | |
+---------+----------------------+------+-----+---------+-------+
Se trata de una tabla donde guardo el stock de un material. En el campo stk_ini recojo el stock inicial, en stk_inc las entradas de stock que preveo para un período (incomings) y en stk_out las salidas de stock para ese mismo período (outgoings). Después ejecuto la consulta con el fin de obtener el stock final previsto para ese período como diferencia del inicial más las entradas menos las salidas previstas:
stk_ini,
stk_inc,
stk_out,
(stk_ini + stk_inc - stk_out) AS stk_fin
FROM
stk_stockmonth;
Pues bien, aquí es donde obtenía un resultado inesperado:
| stk_ini | stk_inc | stk_out | stk_fin |
+---------+---------+---------+----------------------+
| 100 | 50 | 180 | 18446744073709551586 |
+---------+---------+---------+----------------------+
¿Cómo que 18446…? Simplemente tenía que devolverme -30 (entraría en rotura de stock, sí, pero ese es otro tema
), ¿dónde estaba el problema pues? ¿Cómo demonios hemos obtenido este astronómico stock digno del más puro milagro al estilo de la multiplicación de los panes y los peces? Fácil. El motivo reside en los tipos de datos. Como los tres campos con los que está operando son smallint(5) unsigned, MySQL asume que el resultado también será un unsigned, cuando en este caso no tiene por qué serlo. La solución pasaría pues por cambiar los tipos de datos y dejarlos en signed, pero no me convence porque realmente para mí esos datos tienen que ser unsigned y no quiero reducir a la mitad el número máximo que puedo almacenar en esos campos por añadirle un signo que no necesito.
Pues aquí está la solución que encontré:
stk_ini,
stk_inc,
stk_out,
CAST((stk_ini + stk_inc - stk_out) AS SIGNED) AS stk_fin
FROM
stk_stockmonth;
Haciendo un casting a número con signo, MySQL ya nos muestra el resultado esperado:
| stk_ini | stk_inc | stk_out | stk_fin |
+---------+---------+---------+---------+
| 100 | 50 | 180 | -30 |
+---------+---------+---------+---------+
En principio es lógico que MySQL asuma que si en la operación sólo participan números unsigned el resultado también lo vaya a ser, pero me pregunto si no podría estar algo más optimizado para prever que pudiera no ser así y efectuar un casting de manera automática.
Últimos comentarios
RSS