sql >> Database >  >> RDS >> Database

SQL FLOAT:3 punten die u zullen helpen rare rekenfouten te vermijden

Heb je ooit gedacht dat SQL fout kan zijn in wiskunde? Het klinkt gek. Maar als je het gegevenstype SQL FLOAT hebt gebruikt, ben je misschien de problemen tegengekomen die ik je ga laten zien.

Overweeg dit. 0,1 + 0,2 zou 0,3 moeten zijn, toch? Maar bekijk dit eens met het gegevenstype SQL FLOAT.

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

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

Het juiste resultaat is 1. Maar controleer figuur 1.

Heb ik nu je aandacht? Ik hoop het echt. Het is best eng om afhankelijk te zijn van een systeem dat ons geen correcte wiskunde geeft. Maar dit artikel helpt je dit te voorkomen.

Er is wat werk te doen. We moeten uitgaan van waar een FLOAT-gegevenstype over gaat.

Wat is SQL FLOAT-gegevenstype?

SQL FLOAT-gegevenstype is een benaderend numeriek gegevenstype dat wordt gebruikt voor getallen met drijvende komma. Ze kunnen zeer grote of zeer kleine aantallen opslaan. Ze worden ook gebruikt voor berekeningen die snelle verwerkingstijden vereisen.

Dit alles gaat ten koste van het verlies aan precisie. Verder kun je niet zeggen waar de komma na de berekening wordt geplaatst - het zweeft . Ondertussen hebben exacte cijfers zoals DECIMAL een vaste positie achter de komma.

Hoe u een SQL FLOAT-gegevenstype declareert

De syntaxis is FLOAT[(n)], waarbij n is het aantal bits dat wordt gebruikt om de mantisse van een getal met drijvende komma op te slaan in wetenschappelijke notatie. Dat bepaalt ook de precisie en de opslaggrootte. De mogelijke waarden voor n zijn tussen 1 en 53. Merk op dat n is optioneel.

Hier is een voorbeeld:

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

Als u n . niet opgeeft , de standaardwaarde is 53. Dat is ook de maximale waarde. Verder is FLOAT(53) een drijvende-kommagetal met dubbele precisie of binair64. Naast het gebruik van FLOAT(53), kunt u het ook als DUBBELE PRECISIE declareren.

De volgende 3 verklaringen zijn functioneel equivalent:

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

De tabel toont het aantal bits en de bijbehorende opslaggrootte.

Waarde van n Opslaggrootte
1 tot 24 4 bytes
25 tot 53 8 bytes

Is SQL FLOAT en REAL hetzelfde?

REAL is ook FLOAT(24). Het wordt ook wel single-precision of binary32 genoemd.

Waarom het belangrijk is om dit te weten

Als u weet dat dit een numeriek getal is, kunt u het niet gebruiken voor berekeningen die nauwkeurigheid vereisen. Ben je ook bezig met opslag en geheugen? Gebruik REAL of FLOAT(24) als u geen te grote of te kleine waarden nodig heeft.

Wat zijn de verschillen tussen FLOAT en DECIMAL?

FLOAT is een benaderend getal. DECIMAAL is een exact getal. Hier is een samenvatting van de verschillen in een tabel:

FLOAT DECIMAAL
Decimaal punt Kan overal in het cijfer worden geplaatst Vaste positie
Maximale limiet 38 cijfers of 99,999,999,999,999,999,999,999,999,999,999,999,999, FLOAT(53) heeft een maximum bereik van 1.79E+308 of 179 gevolgd door 306 nullen
Opslag Maximaal 8 bytes Maximaal 17 bytes
Berekeningsresultaat Bij benadering Exact
Vergelijkingscontroles Gebruik geen =of <>. Vermijd bij afronding =of <> operatoren. Goed om af te ronden

Je zag in figuur 1 al hoe het berekenen van een FLOAT-getal rare resultaten kan hebben. Als u het gegevenstype als volgt wijzigt in DECIMAAL:

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 

Het resultaat zal correct zijn.
Het gebruik van een ongelijkheidsoperator is ook een probleem. Bekijk de lus hieronder.

DECLARE @floatValue FLOAT(1) = 0.0

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

Wat denk je? Zie Afbeelding 2 hieronder.

Boom! Oneindige lus! De ongelijkheidsvoorwaarde zal altijd waar zijn. De logische keuze is dus om het type te veranderen in DECIMAAL.

DECLARE @decimalValue DECIMAL(2,1) = 0.0

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

De bovenstaande code stopt zeker wanneer @decimalValue is gelijk aan 5,0. Bekijk het zelf in Afbeelding 3 hieronder.

Leuk! Maar als je nog steeds op FLOAT aandringt, zal dit prima werken zonder de oneindige lus.

DECLARE @floatValue FLOAT(1) = 0.0

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

Ondertussen is het afronden ook af. Overweeg het volgende:

DECLARE @value FLOAT(2) = 1.15

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

In plaats van 1.20 resulteert de code in 1.1. Maar als u DECIMAAL gebruikt, is het resultaat correct.

DECLARE @value DECIMAL(3,2) = 1.15

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

Als FLOAT correct is en DECIMAAL niet

Zijn exacte cijfers NIET altijd zo exact? Om dit probleem te reproduceren, gebruiken we een berekening en keren we deze om. Laten we eerst de gegevens voorbereiden.

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)

De bovenstaande tabel gebruikt vaste waarden voor de eerste 2 kolommen. De derde kolom bevat de berekening. Ten slotte zal de vierde, een berekende kolom, de omgekeerde berekening uitvoeren. Het juiste resultaat in de berekende kolom moet 1 zijn.

Laten we nu, om het te vergelijken met FLOAT, een vergelijkbare tabel en gegevens maken.

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)

Laten we een vraag stellen.

SELECT * FROM ApproxNumerics1
SELECT * FROM ExactNumerics1

De resultaten? Bekijk figuur 4.

Wat is hier gebeurd? FLOAT had het goed, maar DECIMAL niet. Er is iets misgegaan.

IMPLICIETE CONVERSIE DOET HET OPNIEUW

Impliciete conversie vindt plaats omdat SQL vergevingsgezind is. Wanneer verschillende datatypes in een berekening worden gebruikt, probeert SQL Server deze achter onze rug om te converteren met behulp van impliciete conversie.

Is er echt een conversie gebeurd? Trouwens, elke kolom in de ExactNumerics1 tabel is een DECIMAAL.

Laten we eens kijken naar de tabelstructuur van de ExactNumerics1 tabel in SQL Server Management Studio:

Let op het rode omkaderde gebied in figuur 3. De berekende kolom is een DECIMAAL (30,17), geen DECIMAAL (8,4). Volgens officiële documentatie zijn 2 DECIMALE kolommen met verschillende precisie en schaal 2 verschillende gegevenstypen . Kijk hier zelf. Vanwege het verschil is een conversie vereist. Dus impliciete conversie speelt een rol.

Wat als ze anders zijn en er een impliciete conversie heeft plaatsgevonden?

Nogmaals, op basis van de officiële documentatie kan een verlies van precisie of schaal optreden tijdens impliciete conversie . Er is dus een expliciete CAST vereist. Let op het DECIMAL-gegevenstype in de conversietabel in die referentie.

Hier is net wat verlies geleden. Als de berekende kolom ook DECIMAAL (8,4) is, vindt de impliciete conversie niet plaats.

Volg de officiële documentatie om de impliciete conversie te voorkomen. De tabelstructuur had er zo uit moeten zien:

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
)

De expliciete CAST in de berekende kolom zorgt ervoor dat de gegevenstypen consistent zijn. Als we ook deze structuur volgen en dezelfde gegevens invoeren, is het resultaat correct. Bekijk de nieuwe uitvoer in Afbeelding 6 hieronder.

Uiteindelijk zullen exacte cijfers niet exact zijn als er een impliciete conversie plaatsvindt tussen 2 of meer DECIMALE waarden.

Waarom het belangrijk is om dit te weten

Het geeft je een idee van wat je nodig hebt voor je tabellen en variabelen. Bovendien kan impliciete conversie zelfs exacte cijfers gek maken. Definieer dus expliciet de precisie en schaal en wees er consistent mee in uw berekeningen.

Moet ik SQL FLOAT gebruiken voor financiële gegevens?

Bij het berekenen van percentages in elk segment van een taartgrafiek, moet de som 100% zijn. Totalen in de samenvatting en gedetailleerde rapporten moeten ook consistent zijn. Als de nauwkeurigheid van de resultaten cruciaal is, zal een benaderend gegevenstype zoals FLOAT het werk niet doen. De logische keuze hiervoor is DECIMAAL.

Maar er blijft een vraag.

Wanneer moet je FLOAT gebruiken?

Gebruik FLOAT voor gegevens waarvoor astronomische waarden nodig zijn, zoals afstanden tussen sterrenstelsels. Ondertussen zal het DECIMAL-gegevenstype een rekenkundige overloop krijgen met dit type gegevens. Kleine waarden zoals de diameter van een atoomkern passen ook met FLOAT. Wetenschappelijke gegevens en andere waarden die geen precisie vereisen, kunnen ook profiteren van FLOAT.

Waarom het belangrijk is om dit te weten

We zeggen niet dat FLOAT slecht is en DECIMAL goed of omgekeerd. Als u de juiste gebruiksscenario's voor elk kent, krijgt u en uw gebruikers de beoogde resultaten. En nogmaals, u wilt toch dat uw gebruikers tevreden zijn, toch?

Conclusie

Uiteindelijk willen we allemaal ons werk doen en er goed in zijn. Wiskunde zal altijd deel uitmaken van ons werk. En als we de juiste numerieke gegevenstypen kennen, kunnen we er ook mee omgaan. Het is niet moeilijk als je weet wat je doet.

Ik hoop dat dit artikel je heeft geholpen vreemde wiskunde in SQL Server te vermijden.

Heb je nog iets toe te voegen? Laat het ons dan weten in het gedeelte Opmerkingen. Deel dit ook op je favoriete sociale mediaplatforms.


  1. PostgreSQL-tekst [][]-type en Java-type toewijzen

  2. Kan een gedistribueerde transactie niet starten

  3. Hoe de LTRIM()-functie werkt in MySQL

  4. Hoe maak je een back-up van je Moodle MySQL-database