Error 802 en una consulta

1 comentario

En el foro preguntaron:

¿Por qué esta consulta:

Listado 1.

SELECT
   192.76110000000000 / 2.2200
FROM
   RDB$DATABASE

muestra el error: -802 Arithmetic overflow or division by zero has occurred.?

El problema, tal y como se explicó en el artículo Usando NUMERIC y DECIMAL es debido a la forma en que el Firebird define a los números escalados (también llamados de coma fija o de punto fijo; son sinónimos).

En Firebird los números escalados tienen el formato:

p.s

donde p indica la precisión y s indica la escala (s=scale, en inglés).

La precisión máxima en Firebird, hasta la versión 2.5.4 es de 18. La escala siempre debe ser menor o igual a la precisión, nunca puede ser mayor.

¿Cuál es la precisión del resultado cuándo se hace una división?

Siempre es 18.

¿Cuál es la escala del resultado cuándo se hace una división?

Siempre es la suma de la escala del numerador más la escala del denominador.

¿Cuál fue el problema en la división que dio origen a este artículo?

Que el numerador (192.76110000000000) tiene una escala de 14 y el denominador (2.2200) tiene una escala de 4. El resultado de la división por lo tanto tendrá una escala de 18 (es decir, 14 + 4). Ya está en el límite máximo permitido.

La división anterior sí funcionará si la parte entera del numerador es pequeña, por ejemplo:

Listado 2.

SELECT
   19.76110000000000 / 2.2200
FROM
   RDB$DATABASE

La parte entera del numerador es 19 y allí sí funciona.

¿Por qué funciona cuándo el numerador es pequeño?

Si la precisión es 18 entonces el número se guarda como un BIGINT. Los números de tipo BIGINT pueden encontrarse entre -2 * 10 ^ 63 y 2 * 10 ^ 63 – 1. O sea, entre -9.223.372.036.854.775.808 y +9.223.372.036.854.775.807

Si la escala es 18 entonces el Firebird divide a ese número por 1.000.000.000.000.000.000 o sea un 1 seguido por 18 ceros.

Al dividir 9.223.372.036.854.775.807 por 1.000.000.000.000.000.000 obtenemos: 9,223372036854775807

Por lo tanto, si la precisión es 18 y la escala es 18 (como en el Listado 1.) el resultado de la división debe ser 9,22 ó menor para no obtener un error de overflow.

Pero en el Listado 1. el resultado de la división es 86,829321…., ese número es mayor que 9,22 y entonces lo que se obtiene es un … error de overflow.

¿Y si necesitamos hacer sí o sí la división del Listado 1., cómo lo solucionamos?

Tenemos varias alternativas:

Listado 3.

SELECT
   192.7611 / 2.2200
FROM
   RDB$DATABASE

Le quitamos los ceros al numerador y funciona sin problemas porque ahora la escala es de 8.

Listado 4.

SELECT
   192.76110000000000 / 2.22
FROM
   RDB$DATABASE

Le quitamos los ceros al denominador y funciona sin problemas porque ahora la escala es de 16.

Listado 5.

SELECT
   CAST(192.76110000000000 AS NUMERIC(18, 13)) / 2.2200
FROM
   RDB$DATABASE

Empleamos la función CAST() para disminuir la escala del numerador y funcionó sin problemas.

Listado 6.

SELECT
   CAST(192.761100000000000 AS FLOAT) / 2.2200
FROM
   RDB$DATABASE

Empleamos la función CAST() para cambiar el tipo de datos del numerador a FLOAT y funcionó sin problemas.

Conclusión:

Tal y como se dijo en el artículo antes citado: “La precisión del resultado siempre es 18. La escala del resultado es la suma de las escalas del dividendo y del divisor. Esto implica que la escala del cociente siempre será mayor que las del dividendo y del divisor. Debes tener cuidado con esto si los números que empleas tienen muchos decimales o podrías sobrepasar la escala máxima que es 18 o necesitar una precisión mayor que 18 y en ambos casos eso te ocasionaría un error de overflow.”

Lo importante a recordar aquí por lo tanto es que si divides números que tienen muchos decimales puedes sobrepasar la escala máxima de 18 o necesitar una precisión mayor que 18 y en ambos casos tendrás un error de overflow.

Hay varios métodos para realizar la división de todos modos, algunos de los cuales vimos en los últimos listados de arriba (hay varios más, pero ya tienes la idea).

Artículos relacionados:

Usando NUMERIC y DECIMAL

El índice del blog Firebird21

El foro del blog Firebird21

Anuncios

Eligiendo el tipo de dato numérico que se usará en una columna

13 comentarios

Cuando defines que los datos que se guardarán en una columna serán de tipo numérico debes decidir también cual de los tipos numéricos que posee el Firebird emplearás. Estas son tus opciones:

  • SMALLINT
  • INTEGER
  • BIGINT
  • NUMERIC
  • DECIMAL
  • FLOAT
  • DOUBLE PRECISION

Como ves, tienes 7 posibilidades, cada una de ellas será la más apropiada para alguna circunstancia, así que ahora debes decidirte por una de esas 7 alternativas.

¿Necesitas guardar números exactos o números aproximados?

A veces, el número que guardas en un columna debe ser exacto, no se admite por ningún motivo que sea aproximado. Ese es el caso del dinero. Allí, tendrás que saber hasta el último centavo cuanto dinero te debe un cliente. O cuanto dinero le cobraste a ese cliente. No puedes responder con algo como: “aproximadamente 100 dólares”, sino que debes responder con el monto exacto.

Otras veces, un número aproximado es apropiado, por ejemplo: ¿cuánto pesó la última bolsa de harina que se puso en la balanza? si respondes con: 52 kilogramos y 245 gramos estará ok, si no eran 245 gramos sino 246 gramos ó 247 gramos no hay problema. Lo mismo que si en lugar de ser 245 gramos eran en realidad 245,1304 gramos. No hay problema.

Necesitarás guardar valores exactos cuando estés tratando con cantidades (dinero, unidades, cajas, precios, etc.) o sea algo que se puede contar y podrás usar valores aproximados cuando puedas medirlo (longitud, volumen, peso, presión, voltaje, etc.)

¿Puedes contarlo? entonces necesitarás números exactos

¿Puedes medirlo? entonces podrás usar números aproximados

¿Cuáles son los tipos de datos numéricos exactos?

  • SMALLINT
  • INTEGER
  • BIGINT
  • NUMERIC
  • DECIMAL

Estos tipos de datos pueden guardar números enteros y también números con decimales. La precisión (representada con la letra “p”) es la cantidad de dígitos significativos. La escala (representada con la letra “s”) es la cantidad de dígitos que se encuentran a la derecha de la coma decimal.

Por ejemplo: DECIMAL(8, 3) significa ppppp.sss

Aquí, hay 8 dígitos en total y como 3 de ellos corresponden a la parte decimal eso implica que hay 5 en la parte entera.

¿Cuáles son los tipos de datos numéricos aproximados?

  • FLOAT
  • DOUBLE PRECISION

Estos tipos de datos guardan números decimales y números con exponentes.

Los últimos decimales de estos números pueden variar cuando se hacen operaciones matemáticas, por eso nunca se los debe usar para guardar en ellos cantidades que deben ser exactas.

Rangos de valores

TIPO DE DATOS       DESDE              HASTA                 .
SMALLINT            -32768             32767 
INTEGER             -2147483648        2147483647 
BIGINT              -2 * 10 ^ 63       2 * 10 ^ 63 - 1 
NUMERIC             -2 * 10 ^ 63       2 * 10 ^ 63 - 1 
DECIMAL             -2 * 10 ^ 63       2 * 10 ^ 63 - 1 
FLOAT               1,175 * 10 ^ -38   3,402 * 10 ^ 38 
DOUBLE PRECISION    2,225 * 10 ^ -308  1,797 * 10 ^ 308

Ejemplo que muestra el error de precisión cuando se usa FLOAT

Este ejemplo nos mostrará por qué no debemos guardar valores que deben ser exactos en una columna de tipo FLOAT

UPDATE
   PRODUCTOS
SET
   PRD_PREVTA = 1.23456
WHERE
   PRD_IDENTI = 215

Este comando UPDATE guarda el número 1.23456 en la columna PRD_PREVTA (precio de venta) de la tabla PRODUCTOS para el producto cuyo identificador es 215.

Ahora, queremos ver el precio de venta que guardamos con el comando UPDATE. Deberíamos ver 1.23456 ¿será eso lo que veremos?

SELECT
   PRD_PREVTA
FROM
   PRODUCTOS
WHERE
   PRD_IDENTI = 215

La imagen de abajo es lo que obtenemos al ejecutar este SELECT.

PREVTA

(haciendo click en la imagen la verás más grande)

Pues no, no obtuvimos 1.23456 sino un número un poco mayor, un número diferente.

Mucho cuidado con esto, cuando se trata de dinero esta clase de errores son totalmente inaceptables.

Artículo relacionado:

https://firebird21.wordpress.com/2013/03/04/entendiendo-numeric-y-decimal/