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

SQL FLOAT:3 punkter, der hjælper dig med at undgå underlige matematiske fejl

Har du nogensinde troet, at SQL kan være forkert i matematik? Det lyder skørt. Men hvis du har brugt SQL FLOAT-datatypen, er du muligvis stødt på de problemer, jeg er ved at vise dig.

Overvej dette. 0,1 + 0,2 burde være 0,3, ikke? Men tjek dette ud ved hjælp af SQL FLOAT-datatypen.

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

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

Det korrekte resultat er 1. Men tjek figur 1.

Har jeg din opmærksomhed nu? Det håber jeg bestemt. Det er ret skræmmende at være afhængig af et system, der ikke giver os korrekt matematik. Men denne artikel hjælper dig med at undgå dette.

Der er noget arbejde at gøre. Vi skal tage udgangspunkt i, hvad en FLOAT-datatype handler om.

Hvad er SQL FLOAT-datatype?

SQL FLOAT-datatypen er en omtrentlig numerisk datatype, der bruges til flydende kommatal. De kan gemme meget store eller meget små tal. De bruges også til beregninger, der kræver hurtige behandlingstider.

Alt dette kommer på bekostning af tabet af præcision. Yderligere kan du ikke sige, hvor decimaltegnet vil blive placeret efter beregningen - det flyder . I mellemtiden vil nøjagtige tal som DECIMAL have en fast decimalposition.

Sådan erklærer du en SQL FLOAT-datatype

Syntaksen er FLOAT[(n)], hvor n er antallet af bit, der bruges til at gemme mantissen af ​​et flydende kommatal i videnskabelig notation. Det dikterer også præcisionen og opbevaringsstørrelsen. De mulige værdier for n er mellem 1 og 53. Bemærk at n er valgfrit.

Her er et eksempel:

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

Hvis du ikke angiver n , standarden er 53. Det er også den maksimale værdi. Ydermere er FLOAT(53) et flydende tal med dobbelt præcision eller binært64. Udover at bruge FLOAT(53), kan du også erklære det som DOBBELT PRÆCISION.

Følgende 3 erklæringer er funktionelt ækvivalente:

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

Tabellen viser antallet af bit og den tilsvarende lagerstørrelse.

Værdi af n Lagerstørrelse
1 til 24 4 bytes
25 til 53 8 bytes

Er SQL FLOAT og REAL det samme?

REAL er også FLOAT(24). Det omtales også som single-precision eller binary32.

Hvorfor er det vigtigt at vide dette

At vide, at dette er et omtrentligt tal, vil forhindre dig i at bruge det til beregninger, der kræver nøjagtighed. Er du også optaget af opbevaring og hukommelse? Brug REAL eller FLOAT(24), hvis du ikke har brug for for store eller for små værdier.

Hvad er forskellene mellem FLOAT og DECIMAL?

FLOAT er et omtrentligt tal. DECIMAL er et nøjagtigt tal. Her er en oversigt over forskellene i en tabel:

FLYDE DECIMAL
Decimaltegn Kan placeres hvor som helst i cifferet Fast position
Maksimal grænse 38 cifre eller 99,999,999,999,999,999,999,999,999,999,999,999,999 FLOAT(53) har et maksimalt interval på 1,79E+308 eller 179 efterfulgt af 306 nuller
Lagring Maksimalt 8 bytes Maksimalt 17 bytes
Beregningsresultat Omtrentlig Nøjagtig
Sammenligningstjek Brug ikke =eller <>. Undgå ved afrunding =eller <> operatorer. God til afrunding

Du har allerede set i figur 1, hvordan beregning af et FLOAT-nummer kan give mærkelige resultater. Hvis du ændrer datatypen til DECIMAL på denne måde:

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 vil være korrekt.
Det er også et problem at bruge en ulighedsoperatør. Tjek løkken nedenfor.

DECLARE @floatValue FLOAT(1) = 0.0

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

Hvad synes du? Se figur 2 nedenfor.

Bom! Uendelig løkke! Ulighedsbetingelsen vil altid være sand. Så det logiske valg er at ændre typen til DECIMAL.

DECLARE @decimalValue DECIMAL(2,1) = 0.0

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

Ovenstående kode vil helt sikkert stoppe, når @decimalValue er lig med 5,0. Se selv i figur 3 nedenfor.

Pæn! Men hvis du stadig insisterer på FLOAT, vil dette fungere fint uden den uendelige loop.

DECLARE @floatValue FLOAT(1) = 0.0

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

I mellemtiden er afrunding også slukket. Overvej følgende:

DECLARE @value FLOAT(2) = 1.15

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

I stedet for 1.20 resulterer koden til 1.1. Men hvis du bruger DECIMAL, vil resultatet være korrekt.

DECLARE @value DECIMAL(3,2) = 1.15

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

Når FLOAT er Korrekt, og DECIMAL er Ikke

Er nøjagtige tal IKKE så nøjagtige hele tiden? For at reproducere dette problem vil vi bruge en beregning, og så vender vi den om. Lad os først forberede dataene.

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 ovenfor vil bruge faste værdier for de første 2 kolonner. Den tredje kolonne vil have beregningen. Endelig vil den fjerde, som er en beregnet kolonne, udføre den omvendte beregning. Det korrekte resultat i den beregnede kolonne skal være 1.

Nu, for at sammenligne det med FLOAT, lad os oprette en lignende tabel og 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)

Lad os forespørge.

SELECT * FROM ApproxNumerics1
SELECT * FROM ExactNumerics1

Resultaterne? Se figur 4.

Hvad skete der her? FLOAT fik det rigtigt, men DECIMAL gjorde det ikke. Noget er gået galt.

IMPLICIT KONVERTERING GØR DET IGEN

Implicit konvertering sker, fordi SQL er tilgivende. Når forskellige datatyper bruges i en beregning, forsøger SQL Server at konvertere dem ved hjælp af implicit konvertering bag vores ryg.

Er der virkelig sket en konvertering? Desuden er hver kolonne i ExactNumerics1 tabel er en DECIMAAL.

Lad os tjekke tabelstrukturen for ExactNumerics1 tabel i SQL Server Management Studio:

Bemærk det røde felt i figur 3. Den beregnede kolonne er en DECIMAL(30,17), ikke en DECIMAL(8,4). Ifølge officiel dokumentation er 2 DECIMAL kolonner med forskellig præcision og skala 2 forskellige datatyper . Se selv her. På grund af forskellen kræves en konvertering. Så implicit konvertering spiller ind.

Hvad hvis de er forskellige, og der var sket en implicit konvertering?

Igen, baseret på den officielle dokumentation, kan et tab af præcision eller skala ske under implicit konvertering . Der kræves således en eksplicit CAST. Bemærk DECIMAL-datatypen i konverteringstabellen i den reference.

Der er lige sket et eller andet tab her. Hvis den beregnede kolonne også er DECIMAL(8,4), sker den implicitte konvertering ikke.

Følg den officielle dokumentation for at undgå den implicitte konvertering. Tabelstrukturen skulle have været sådan her:

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 eksplicitte CAST i den beregnede kolonne sikrer, at datatyperne er konsistente. Hvis vi også følger denne struktur og indsætter de samme data, bliver resultatet korrekt. Se det nye output i figur 6 nedenfor.

Til sidst vil nøjagtige tal ikke være nøjagtige, hvis en implicit konvertering finder sted mellem 2 eller flere DECIMAL-værdier.

Hvorfor er det vigtigt at vide dette

Det giver dig en idé om, hvad du skal bruge til dine tabeller og variabler. Desuden kan implicit konvertering få selv nøjagtige numeriske til at gå amok. Så definer eksplicit præcisionen og skalaen, og vær i overensstemmelse med det i dine beregninger.

Skal jeg bruge SQL FLOAT til finansielle data?

Når man beregner procenter i hvert stykke af en cirkeldiagram, skal summen være 100 %. Totalerne i oversigten og detaljerede rapporter bør også være konsistente. Hvis nøjagtigheden af ​​resultaterne er afgørende, vil en omtrentlig datatype som FLOAT ikke klare opgaven. Det logiske valg for dette er DECIMAL.

Men et spørgsmål er tilbage.

Hvornår skal du bruge FLOAT?

Brug FLOAT til data, der kræver astronomiske værdier som afstande mellem galakser. I mellemtiden vil DECIMAL-datatypen lide af et aritmetisk overløb med denne type data. Små værdier som diameteren af ​​en atomkerne vil også passe med FLOAT. Videnskabelige data og andre værdier, der ikke kræver præcision, kan også drage fordel af FLOAT.

Hvorfor er det vigtigt at vide dette

Vi siger ikke, at FLOAT er dårligt, og DECIMAL er godt eller omvendt. At kende de korrekte use cases for hver vil give dig og dine brugere de tilsigtede resultater. Og så igen, du vil have dine brugere glade, ikke?

Konklusion

Ved udgangen af ​​dagen ønsker vi alle at udføre vores job og være gode til dem. Matematik vil altid være en del af vores job. Og at kende de korrekte numeriske datatyper vil også hjælpe os med at håndtere det. Det er ikke svært, hvis du ved, hvad du laver.

Jeg håber, at denne artikel har hjulpet dig med at undgå underlig matematik i SQL Server.

Har du andet at tilføje? Så fortæl os det i kommentarfeltet. Del dette også på dine foretrukne sociale medieplatforme.


  1. Hvad er den bedste måde at håndtere DBNull's på

  2. streng bogstavelig talt for lang - hvordan tildeles lange xml-data til clob-datatype i oracle 11g r2

  3. Sådan konverteres en postgres-database til sqlite

  4. Sådan starter parallelle planer op – del 1