sql >> Base de Datos >  >> RDS >> Database

SQL FLOAT:3 puntos que te ayudarán a evitar errores matemáticos extraños

¿Alguna vez has pensado que SQL puede estar mal en matemáticas? Suena loco. Pero si ha utilizado el tipo de datos SQL FLOAT, es posible que se haya encontrado con los problemas que estoy a punto de mostrarle.

Considera esto. 0.1 + 0.2 debería ser 0.3, ¿verdad? Pero mira esto usando el tipo de datos SQL FLOAT.

DECLARE @f1 FLOAT = 0.1
DECLARE @f2 FLOAT = 0.2

SELECT CASE WHEN @f1 + @f2 = .3 THEN 1 ELSE 0 END

El resultado correcto es 1. Pero revisa la Figura 1.

¿Tengo tu atención ahora? Seguro espero eso. Da bastante miedo depender de un sistema que no nos dará las matemáticas correctas. Pero este artículo te ayudará a evitarlo.

Hay algo de trabajo que hacer. Tenemos que empezar por lo que es un tipo de datos FLOAT.

¿Qué es el tipo de datos SQL FLOAT?

El tipo de datos SQL FLOAT es un tipo de datos numérico aproximado que se utiliza para números de punto flotante. Pueden almacenar números muy grandes o muy pequeños. También se utilizan para cálculos que requieren tiempos de procesamiento rápidos.

Todo esto tiene el costo de la pérdida de precisión. Además, no puede decir dónde se colocará el punto decimal después del cálculo:flota . Mientras tanto, números exactos como DECIMAL tendrán una posición de punto decimal fija.

Cómo declarar un tipo de datos SQL FLOAT

La sintaxis es FLOAT[(n)], donde n es el número de bits utilizados para almacenar la mantisa de un número de coma flotante en notación científica. Eso también dicta la precisión y el tamaño de almacenamiento. Los valores posibles para n están entre 1 y 53. Tenga en cuenta que n es opcional.

He aquí un ejemplo:

DECLARE @floatValue1 FLOAT;   -- Float variable without the number of bits
DECLARE @floatValue2 FLOAT(3) -- Float variable with 3 bits 

Si no especifica n , el valor predeterminado es 53. Ese también es el valor máximo. Además, FLOAT(53) es un número de coma flotante de doble precisión o binary64. Además de usar FLOAT(53), también puede declararlo como DOUBLE PRECISION.

Las siguientes 3 declaraciones son funcionalmente equivalentes:

DECLARE @double1 FLOAT(53); 
DECLARE @double2 FLOAT;
DECLARE @double3 DOUBLE PRECISION;

La tabla muestra el número de bits y el tamaño de almacenamiento correspondiente.

Valor de n Tamaño de almacenamiento
1 a 24 4 bytes
25 a 53 8 bytes

¿SQL FLOAT y REAL son lo mismo?

REAL también es FLOAT(24). También se conoce como precisión simple o binario32.

Por qué es importante saber esto

Saber que se trata de un valor numérico aproximado le impedirá utilizarlo para cálculos que requieran precisión. ¿También te preocupa el almacenamiento y la memoria? Utilice REAL o FLOAT(24) si no necesita valores demasiado grandes o demasiado pequeños.

¿Cuáles son las diferencias entre FLOAT y DECIMAL?

FLOAT es un valor numérico aproximado. DECIMAL es un número exacto. Aquí hay un resumen de las diferencias en una tabla:

Operadores
FLOTACIÓN DECIMALES
Punto decimal Se puede colocar en cualquier parte del dígito Posición fija
Límite máximo 38 dígitos o 99.999.999.999.999.999.999.999.999.999.999.999.999 FLOAT(53) tiene un rango máximo de 1.79E+308 o 179 seguido de 306 ceros
Almacenamiento Máximo de 8 bytes Máximo de 17 bytes
Resultado computacional Aproximado Exacto
Comprobaciones de comparación No use =o <>. Evitar al redondear=o <>. Bueno para redondear

Ya vio en la Figura 1 cómo calcular un número FLOAT puede tener resultados extraños. Si cambia el tipo de datos a DECIMAL así:

DECLARE @d1 DECIMAL(2,1) = 0.1
DECLARE @d2 DECIMAL(2,1) = 0.2

SELECT CASE WHEN @d1 + @d2 = 0.3 THEN 1 ELSE 0 END 

El resultado será correcto.
Usar un operador de desigualdad también es un problema. Echa un vistazo al bucle a continuación.

DECLARE @floatValue FLOAT(1) = 0.0

WHILE @floatValue <> 5.0
BEGIN
	PRINT @floatValue;
	SET @floatValue += 0.1;
END 

¿Qué piensas? Consulte la Figura 2 a continuación.

¡Auge! ¡Bucle infinito! La condición de desigualdad siempre se cumplirá. Entonces, la elección lógica es cambiar el tipo a DECIMAL.

DECLARE @decimalValue DECIMAL(2,1) = 0.0

WHILE @decimalValue <> 5.0
BEGIN
	PRINT @decimalValue;
	SET @decimalValue += 0.1;
END 

El código anterior seguramente se detendrá cuando @decimalValue es igual a 5.0. Compruébelo usted mismo en la Figura 3 a continuación.

¡Lindo! Pero si aún insiste en FLOAT, funcionará bien sin el bucle infinito.

DECLARE @floatValue FLOAT(1) = 0.0

WHILE @floatValue < 5.0
BEGIN
	PRINT @floatValue;
	SET @floatValue += 0.1;
END

Mientras tanto, el redondeo también está apagado. Considere lo siguiente:

DECLARE @value FLOAT(2) = 1.15

SELECT ROUND(@value, 1)  -- This will result to 1.1

En lugar de 1.20, el código da como resultado 1.1. Pero si usa DECIMAL, el resultado será correcto.

DECLARE @value DECIMAL(3,2) = 1.15

SELECT ROUND(@value, 1)  -- This will result in 1.2 or 1.20

Cuando FLOAT es correcto y DECIMAL no lo es

¿Los números exactos NO son tan exactos todo el tiempo? Para reproducir este problema, usaremos un cálculo y luego lo invertiremos. Primero, preparemos los datos.

CREATE TABLE ExactNumerics1
(
	fixed1 DECIMAL(8,4),
	fixed2 DECIMAL(8,4),
	fixed3 DECIMAL(8,4),
	calcValue1 AS fixed3 / fixed1 * fixed2
)
GO

INSERT INTO ExactNumerics1
(fixed1,fixed2,fixed3)
VALUES
(54,0.03,1*54/0.03)

La tabla anterior utilizará valores fijos para las primeras 2 columnas. La tercera columna tendrá el cálculo. Finalmente, la cuarta, que es una columna calculada, hará el cómputo inverso. El resultado correcto en la columna calculada debe ser 1.

Ahora, para compararlo con FLOAT, creemos una tabla y datos similares.

CREATE TABLE ApproxNumerics1
(
	float1 FLOAT(2),
	float2 FLOAT(2),
	float3 FLOAT(2),
	calcValue1 AS float3 / float1 * float2 
)

INSERT INTO ApproxNumerics1
(float1, float2, float3)
VALUES
(54,0.03,1*54/0.03)

Vamos a consultar.

SELECT * FROM ApproxNumerics1
SELECT * FROM ExactNumerics1

¿Los resultados? Mira la Figura 4.

¿Que pasó aquí? FLOAT lo hizo bien, pero DECIMAL no. Algo salió mal.

LA CONVERSIÓN IMPLÍCITA LO HACE DE NUEVO

La conversión implícita ocurre porque SQL perdona. Cuando se utilizan diferentes tipos de datos en un cálculo, SQL Server intenta convertirlos mediante conversión implícita a nuestras espaldas.

¿Realmente sucedió una conversión? Además, cada columna en ExactNumerics1 la tabla es un DECIMAL.

Revisemos la estructura de la tabla de ExactNumerics1 tabla en SQL Server Management Studio:

Observe el área encuadrada en rojo en la Figura 3. La columna calculada es un DECIMAL(30,17), no un DECIMAL(8,4). Según la documentación oficial, 2 columnas DECIMAL con diferente precisión y escala son 2 tipos de datos diferentes . Compruébelo usted mismo aquí. Debido a la diferencia, se requiere una conversión. Entonces, la conversión implícita entra en juego.

¿Qué pasa si son diferentes y se ha producido una conversión implícita?

Nuevamente, según la documentación oficial, puede ocurrir una pérdida de precisión o escala durante la conversión implícita . Por lo tanto, se requiere un CAST explícito. Tenga en cuenta el tipo de datos DECIMAL en la tabla de conversión en esa referencia.

Algunas pérdidas acaban de ocurrir aquí. Si la columna calculada también es DECIMAL(8,4), la conversión implícita no ocurre.

Para evitar la conversión implícita, siga la documentación oficial. La estructura de la tabla debería haber sido así:

CREATE TABLE ExactNumerics2
(
	fixed1 DECIMAL(8,4),
	fixed2 DECIMAL(8,4),
	fixed3 DECIMAL(8,4),
	calcValue1 AS CAST(fixed3 / fixed1 * fixed2 AS DECIMAL(8,4)) -- the explicit CAST
)

El CAST explícito en la columna calculada garantiza que los tipos de datos sean coherentes. Si también seguimos esta estructura e insertamos los mismos datos, el resultado será correcto. Echa un vistazo a la nueva salida en la Figura 6 a continuación.

Eventualmente, los números exactos no serán exactos si ocurre una conversión implícita entre 2 o más valores DECIMAL.

Por qué es importante saber esto

Le da una idea de lo que necesita para sus tablas y variables. Además, la conversión implícita puede hacer que incluso los números exactos se vuelvan locos. Por lo tanto, defina explícitamente la precisión y la escala y sea coherente con ella en sus cálculos.

¿Debería usar SQL FLOAT para datos financieros?

Al calcular porcentajes en cada porción de un gráfico circular, la suma debe ser 100%. Los totales en los informes resumidos y detallados también deben ser consistentes. Si la precisión de los resultados es crucial, un tipo de datos aproximado como FLOAT no funcionará. La opción lógica para esto es DECIMAL.

Pero queda una pregunta.

¿Cuándo debería usar FLOAT?

Use FLOAT para datos que requieran valores astronómicos como distancias entre galaxias. Mientras tanto, el tipo de datos DECIMAL sufrirá un desbordamiento aritmético con este tipo de datos. Valores diminutos como el diámetro de un núcleo atómico también encajarán usando FLOAT. Los datos científicos y otros valores que no requieren precisión también pueden beneficiarse de FLOAT.

Por qué es importante saber esto

No decimos que FLOAT es malo y DECIMAL es bueno o viceversa. Conocer los casos de uso correctos para cada uno le brindará a usted y a sus usuarios los resultados deseados. Y, de nuevo, quieres que tus usuarios estén contentos, ¿verdad?

Conclusión

Al final del día, todos queremos hacer nuestro trabajo y ser buenos en él. Las matemáticas siempre serán parte de nuestros trabajos. Y conocer los tipos de datos numéricos correctos también nos ayudará a lidiar con eso. No es difícil si sabes lo que estás haciendo.

Espero que este artículo le haya ayudado a evitar matemáticas raras en SQL Server.

Tienes algo mas que agregar? Entonces, háznoslo saber en la sección de Comentarios. Comparte esto también en tus plataformas de redes sociales favoritas.