Database
 sql >> Database >  >> RDS >> Database

SQL FLOAT:3 punti che ti aiuteranno a evitare strani errori matematici

Hai mai pensato che SQL possa essere sbagliato in matematica? Sembra pazzesco. Ma se hai utilizzato il tipo di dati SQL FLOAT, potresti aver riscontrato i problemi che sto per mostrarti.

Considera questo. 0,1 + 0,2 dovrebbe essere 0,3, giusto? Ma dai un'occhiata usando il tipo di dati SQL FLOAT.

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

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

Il risultato corretto è 1. Ma controlla la Figura 1.

Ho la tua attenzione ora? Lo spero proprio. È abbastanza spaventoso dipendere da un sistema che non ci darà la matematica corretta. Ma questo articolo ti aiuterà a evitarlo.

C'è del lavoro da fare. Dobbiamo partire da ciò che riguarda un tipo di dati FLOAT.

Che cos'è il tipo di dati SQL FLOAT?

Il tipo di dati SQL FLOAT è un tipo di dati numerico approssimativo utilizzato per i numeri a virgola mobile. Possono memorizzare numeri molto grandi o molto piccoli. Sono utilizzati anche per calcoli che richiedono tempi di elaborazione rapidi.

Tutto ciò a scapito della perdita di precisione. Inoltre, non puoi dire dove verrà posizionato il punto decimale dopo il calcolo:fluttua . Nel frattempo, numeri esatti come DECIMAL avranno una posizione decimale fissa.

Come si dichiara un tipo di dati SQL FLOAT

La sintassi è FLOAT[(n)], dove n è il numero di bit utilizzati per memorizzare la mantissa di un numero a virgola mobile in notazione scientifica. Ciò determina anche la precisione e le dimensioni di archiviazione. I possibili valori per n sono compresi tra 1 e 53. Si noti che n è facoltativo.

Ecco un esempio:

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

Se non specifichi n , il valore predefinito è 53. Questo è anche il valore massimo. Inoltre, FLOAT(53) è un numero a virgola mobile a precisione doppia o binary64. Oltre a usare FLOAT(53), puoi anche dichiararlo come DOPPIA PRECISIONE.

Le seguenti 3 dichiarazioni sono funzionalmente equivalenti:

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

La tabella mostra il numero di bit e la dimensione di archiviazione corrispondente.

Valore di n Dimensioni di archiviazione
da 1 a 24 4 byte
Da 25 a 53 8 byte

SQL FLOAT e REAL sono la stessa cosa?

REAL è anche FLOAT(24). Viene anche chiamato a precisione singola o binary32.

Perché sapere questo è importante

Sapere che si tratta di un valore numerico approssimativo ti impedirà di utilizzarlo per calcoli che richiedono precisione. Ti preoccupi anche di archiviazione e memoria? Usa REAL o FLOAT(24) se non hai bisogno di valori troppo grandi o troppo piccoli.

Quali sono le differenze tra FLOAT e DECIMAL?

FLOAT è un valore numerico approssimativo. DECIMAL è un numero esatto. Ecco un riepilogo delle differenze in una tabella:

GALLEGGIANTE DECIMA
Punto decimale Può essere posizionato ovunque nella cifra Posizione fissa
Limite massimo 38 cifre o 99.999.999.999.999.999.999.999.999.999.999.999.999 FLOAT(53) ha un intervallo massimo di 1,79E+308 o 179 seguito da 306 zeri
Archiviazione Massimo 8 byte Massimo 17 byte
Risultato computazionale Approssimativo Esatto
Controlli di confronto Non utilizzare =o <>. Da evitare durante gli arrotondamenti =o <> operatori. Buono per arrotondare

Hai già visto nella Figura 1 come calcolare un numero FLOAT può avere risultati strani. Se modifichi il tipo di dati in DECIMAL in questo modo:

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 

Il risultato sarà corretto.
Anche l'uso di un operatore di disuguaglianza è un problema. Dai un'occhiata al loop qui sotto.

DECLARE @floatValue FLOAT(1) = 0.0

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

Cosa ne pensi? Vedere la figura 2 di seguito.

Boom! Ciclo infinito! La condizione di disuguaglianza sarà sempre vera. Quindi, la scelta logica è cambiare il tipo in DECIMAL.

DECLARE @decimalValue DECIMAL(2,1) = 0.0

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

Il codice sopra si fermerà sicuramente quando @decimalValue è uguale a 5,0. Guarda tu stesso nella Figura 3 di seguito.

Simpatico! Ma se insisti ancora su FLOAT, funzionerà perfettamente senza il ciclo infinito.

DECLARE @floatValue FLOAT(1) = 0.0

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

Nel frattempo, anche gli arrotondamenti sono disattivati. Considera quanto segue:

DECLARE @value FLOAT(2) = 1.15

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

Invece di 1.20, il codice risulta 1.1. Ma se usi DECIMAL, il risultato sarà corretto.

DECLARE @value DECIMAL(3,2) = 1.15

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

Quando FLOAT è corretto e DECIMAL non lo è

I numeri esatti NON sono sempre così esatti? Per riprodurre questo problema, utilizzeremo un calcolo e quindi lo invertiremo. Per prima cosa, prepariamo i dati.

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 tabella sopra utilizzerà valori fissi per le prime 2 colonne. La terza colonna avrà il calcolo. Infine, la quarta, che è una colonna calcolata, eseguirà il calcolo inverso. Il risultato corretto nella colonna calcolata dovrebbe essere 1.

Ora, per confrontarlo con FLOAT, creiamo una tabella e dati simili.

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)

Interroghiamo.

SELECT * FROM ApproxNumerics1
SELECT * FROM ExactNumerics1

I risultati? Dai un'occhiata alla Figura 4.

Cos'è successo qua? FLOAT ha capito bene, ma DECIMAL no. Qualcosa è andato storto.

LA CONVERSIONE IMPLICITA FA DI NUOVO

La conversione implicita avviene perché SQL perdona. Quando in un calcolo vengono utilizzati tipi di dati diversi, SQL Server tenta di convertirlo utilizzando la conversione implicita alle nostre spalle.

È avvenuta davvero una conversione? Inoltre, ogni colonna in ExactNumerics1 la tabella è un DECIMALE.

Verifichiamo la struttura della tabella di ExactNumerics1 tabella in SQL Server Management Studio:

Notare il riquadro rosso nella Figura 3. La colonna calcolata è un DECIMAL(30,17), non un DECIMAL(8,4). Secondo la documentazione ufficiale, 2 colonne DECIMAL con precisione e scala diverse corrispondono a 2 tipi di dati diversi . Guarda tu stesso qui. A causa della differenza, è necessaria una conversione. Quindi, entra in gioco la conversione implicita.

E se fossero diversi e si fosse verificata una conversione implicita?

Anche in questo caso, in base alla documentazione ufficiale, può verificarsi una perdita di precisione o di scala durante la conversione implicita . Pertanto, è richiesto un CAST esplicito. Nota il tipo di dati DECIMAL nella tabella di conversione in quel riferimento.

Qualche perdita è appena accaduta qui. Se la colonna calcolata è anche DECIMAL(8,4), la conversione implicita non si verifica.

Per evitare la conversione implicita, seguire la documentazione ufficiale. La struttura della tabella avrebbe dovuto essere così:

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
)

Il CAST esplicito nella colonna calcolata garantisce che i tipi di dati siano coerenti. Se seguiamo anche questa struttura e inseriamo gli stessi dati, il risultato sarà corretto. Dai un'occhiata al nuovo output nella Figura 6 di seguito.

Alla fine, i valori numerici esatti non saranno esatti se si verifica una conversione implicita tra 2 o più valori DECIMAL.

Perché sapere questo è importante

Ti dà un'idea di ciò di cui hai bisogno per le tue tabelle e variabili. Inoltre, la conversione implicita può far impazzire anche i numeri esatti. Quindi, definisci in modo esplicito la precisione e la scala e sii coerente con essa nei tuoi calcoli.

Dovrei usare SQL FLOAT per i dati finanziari?

Quando si calcolano le percentuali in ogni sezione di un grafico a torta, la somma dovrebbe essere del 100%. Anche i totali nel riepilogo e nei rapporti dettagliati dovrebbero essere coerenti. Se l'accuratezza dei risultati è fondamentale, un tipo di dati approssimativo come FLOAT non funzionerà. La scelta logica per questo è DECIMAL.

Ma una domanda rimane.

Quando dovresti usare FLOAT?

Usa FLOAT per dati che richiedono valori astronomici come le distanze tra le galassie. Nel frattempo, il tipo di dati DECIMAL subirà un overflow aritmetico con questo tipo di dati. Anche piccoli valori come il diametro di un nucleo atomico si adatteranno usando FLOAT. Anche i dati scientifici e altri valori che non richiedono precisione possono trarre vantaggio da FLOAT.

Perché sapere questo è importante

Non diciamo che FLOAT sia cattivo e DECIMAL sia buono o viceversa. Conoscere i casi d'uso corretti per ciascuno darà a te e ai tuoi utenti i risultati previsti. E poi di nuovo, vuoi che i tuoi utenti siano felici, giusto?

Conclusione

Entro la fine della giornata, tutti noi vogliamo fare il nostro lavoro ed essere bravi a farlo. La matematica farà sempre parte del nostro lavoro. E anche conoscere i tipi di dati numerici corretti ci aiuterà a gestirlo. Non è difficile se sai cosa stai facendo.

Spero che questo articolo ti abbia aiutato a evitare strani calcoli in SQL Server.

Hai altro da aggiungere? Quindi, faccelo sapere nella sezione Commenti. Condividi questo anche sulle tue piattaforme di social media preferite.