sql >> Databasteknik >  >> RDS >> Database

SQL FLOAT:3 poäng som hjälper dig att undvika konstiga matematiska fel

Har du någonsin tänkt på att SQL kan vara fel i matematik? Det låter galet. Men om du har använt datatypen SQL FLOAT kan du ha stött på problemen jag ska visa dig.

Överväg detta. 0,1 + 0,2 borde vara 0,3, eller hur? Men kolla detta med SQL FLOAT-datatypen.

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

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

Rätt resultat är 1. Men kontrollera figur 1.

Har jag din uppmärksamhet nu? Jag hoppas verkligen det. Det är ganska läskigt att vara beroende av ett system som inte ger oss korrekt matematik. Men den här artikeln hjälper dig att undvika detta.

Det finns en del att göra. Vi måste utgå från vad en FLOAT-datatyp handlar om.

Vad är SQL FLOAT Data Type?

SQL FLOAT-datatyp är en ungefärlig numerisk datatyp som används för flyttal. De kan lagra mycket stora eller mycket små antal. De används också för beräkningar som kräver snabba behandlingstider.

Allt detta sker på bekostnad av förlusten av precision. Vidare kan du inte säga var decimalkomma kommer att placeras efter beräkningen – den flyter . Under tiden kommer exakta siffror som DECIMAL att ha en fast decimalpunktsposition.

Hur du deklarerar en SQL FLOAT-datatyp

Syntaxen är FLOAT[(n)], där n är antalet bitar som används för att lagra mantissan för ett flyttal i vetenskaplig notation. Det dikterar också precisionen och lagringsstorleken. De möjliga värdena för n är mellan 1 och 53. Observera att n är valfritt.

Här är ett exempel:

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

Om du inte anger n , är standardvärdet 53. Det är också maxvärdet. Vidare är FLOAT(53) ett flyttal med dubbel precision eller binär64. Förutom att använda FLOAT(53), kan du också deklarera det som DUBBEL PRECISION.

Följande 3 deklarationer är funktionellt likvärdiga:

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

Tabellen visar antalet bitar och motsvarande lagringsstorlek.

Värde på n Lagringsstorlek
1 till 24 4 byte
25 till 53 8 byte

Är SQL FLOAT och REAL samma sak?

REAL är också FLOAT(24). Det kallas också för enkelprecision eller binär32.

Varför är det viktigt att veta detta

Att veta att detta är en ungefärlig numerisk kommer att hindra dig från att använda den för beräkningar som kräver noggrannhet. Är du också intresserad av lagring och minne? Använd REAL eller FLOAT(24) om du inte behöver för stora eller för små värden.

Vilka är skillnaderna mellan FLOAT och DECIMAL?

FLOAT är en ungefärlig siffra. DECIMAL är en exakt siffra. Här är en sammanfattning av skillnaderna i en tabell:

FLYTTA DECIMAL
Decimalkomma Kan placeras var som helst i siffran Fast position
Maximal gräns 38 siffror eller 99,999,999,999,999,999,999,999,999,999,999,999,999 FLOAT(53) har ett maximalt intervall på 1,79E+308 eller 179 följt av 306 nollor
Lagring Max 8 byte Max 17 byte
Beräkningsresultat Ungefärlig Exakt
Jämförelsekontroller Använd inte =eller <>. Undvik vid avrundning =eller <> operatorer. Bra för att avrunda

Du har redan sett i figur 1 hur beräkning av ett FLOAT-nummer kan ge konstiga resultat. Om du ändrar datatypen till DECIMAL så här:

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 

Resultatet blir korrekt.
Att använda en ojämlikhetsoperatör är också ett problem. Kolla in slingan nedan.

DECLARE @floatValue FLOAT(1) = 0.0

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

Vad tror du? Se figur 2 nedan.

bom! Oändlig loop! Ojämlikhetsvillkoret kommer alltid att vara sant. Så det logiska valet är att ändra typen till DECIMAL.

DECLARE @decimalValue DECIMAL(2,1) = 0.0

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

Ovanstående kod kommer säkert att sluta när @decimalValue är lika med 5,0. Se själv i figur 3 nedan.

Trevlig! Men om du fortfarande insisterar på FLOAT kommer detta att fungera utmärkt utan den oändliga loopen.

DECLARE @floatValue FLOAT(1) = 0.0

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

Samtidigt är avrundning också av. Tänk på följande:

DECLARE @value FLOAT(2) = 1.15

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

Istället för 1.20 blir koden 1.1. Men om du använder DECIMAL blir resultatet korrekt.

DECLARE @value DECIMAL(3,2) = 1.15

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

När FLOAT är korrekt och DECIMAL är inte

Är exakta siffror INTE så exakta hela tiden? För att återskapa detta problem använder vi en beräkning, och sedan vänder vi på det. Låt oss först förbereda data.

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)

Tabellen ovan kommer att använda fasta värden för de två första kolumnerna. Den tredje kolumnen kommer att ha beräkningen. Slutligen kommer den fjärde, som är en beräknad kolumn, att göra den omvända beräkningen. Det korrekta resultatet i den beräknade kolumnen bör vara 1.

Nu, för att jämföra det med FLOAT, låt oss skapa en liknande tabell och data.

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)

Låt oss fråga.

SELECT * FROM ApproxNumerics1
SELECT * FROM ExactNumerics1

Resultaten? Kolla in figur 4.

Vad hände här? FLOAT gjorde rätt, men DECIMAL gjorde det inte. Något har gått fel.

IMPLICIT KONVERTERING GÖR DET IGEN

Implicit konvertering sker eftersom SQL är förlåtande. När olika datatyper används i en beräkning, försöker SQL Server konvertera den med hjälp av implicit konvertering bakom vår rygg.

Har en konvertering verkligen skett? Dessutom, varje kolumn i ExactNumerics1 tabellen är en DECIMAL.

Låt oss kontrollera tabellstrukturen för ExactNumerics1 tabell i SQL Server Management Studio:

Lägg märke till det röda området i figur 3. Den beräknade kolumnen är en DECIMAL(30,17), inte en DECIMAL(8,4). Enligt officiell dokumentation är 2 DECIMAL kolumner med olika precision och skala 2 olika datatyper . Se själv här. På grund av skillnaden krävs en konvertering. Så implicit konvertering spelar in.

Tänk om de är olika och en implicit omvandling hade skett?

Återigen, baserat på den officiella dokumentationen kan en förlust av precision eller skala inträffa under implicit konvertering . Därför krävs en explicit CAST. Notera datatypen DECIMAL i konverteringstabellen i den referensen.

Någon förlust hände precis här. Om den beräknade kolumnen också är DECIMAL(8,4) sker inte den implicita konverteringen.

För att undvika den implicita konverteringen, följ den officiella dokumentationen. Tabellstrukturen borde ha varit så här:

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
)

Den explicita CAST i den beräknade kolumnen säkerställer att datatyperna är konsekventa. Om vi ​​även följer denna struktur och lägger in samma data blir resultatet korrekt. Kolla in den nya utgången i figur 6 nedan.

Så småningom kommer exakta siffror inte att vara exakta om en implicit omvandling sker mellan 2 eller fler DECIMAL-värden.

Varför är det viktigt att veta detta

Det ger dig en uppfattning om vad du behöver för dina tabeller och variabler. Dessutom kan implicit konvertering få till och med exakta siffror att bli galna. Så, definiera exakt precisionen och skalan och var konsekvent med det i dina beräkningar.

Ska jag använda SQL FLOAT för finansiella data?

När man beräknar procentsatser i varje del av ett cirkeldiagram bör summan vara 100 %. Summorna i sammanfattningen och detaljerade rapporter bör också vara konsekventa. Om noggrannheten i resultaten är avgörande, kommer en ungefärlig datatyp som FLOAT inte att göra jobbet. Det logiska valet för detta är DECIMAL.

Men en fråga kvarstår.

När ska du använda FLOAT?

Använd FLOAT för data som kräver astronomiska värden som avstånd mellan galaxer. Under tiden kommer datatypen DECIMAL att drabbas av ett aritmetiskt överflöde med denna typ av data. Små värden som diametern på en atomkärna passar också med FLOAT. Vetenskapliga data och andra värden som inte kräver precision kan också dra nytta av FLOAT.

Varför är det viktigt att veta detta

Vi säger inte att FLOAT är dåligt och DECIMAL är bra eller vice versa. Att känna till de korrekta användningsfallen för varje kommer att ge dig och dina användare de avsedda resultaten. Och då igen, du vill att dina användare ska vara nöjda, eller hur?

Slutsats

I slutet av dagen vill vi alla göra vårt jobb och vara bra på dem. Matematik kommer alltid att vara en del av våra jobb. Och att känna till de korrekta numeriska datatyperna kommer också att hjälpa oss att hantera det. Det är inte svårt om du vet vad du gör.

Jag hoppas att den här artikeln har hjälpt dig att undvika konstig matematik i SQL Server.

Har du något mer att tillägga? Låt oss sedan veta i kommentarsektionen. Dela detta också på dina favoritplattformar för sociala medier.


  1. mysqldump bästa praxis:Del 1 – MySQL-förutsättningar

  2. Generera n rader med NULL i PostgreSQL

  3. Flytta till MariaDB Backup

  4. Hur man får tiden från en sträng i MySQL