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

SQL FLOAT:3 Punkte, die Ihnen helfen, seltsame mathematische Fehler zu vermeiden

Haben Sie jemals daran gedacht, dass SQL in Mathematik falsch sein kann? Es klingt verrückt. Aber wenn Sie den SQL FLOAT-Datentyp verwendet haben, sind Sie vielleicht auf die Probleme gestoßen, die ich Ihnen gleich zeigen werde.

Bedenken Sie. 0,1 + 0,2 sollte 0,3 sein, richtig? Aber überprüfen Sie dies mit dem SQL FLOAT-Datentyp.

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

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

Das korrekte Ergebnis ist 1. Aber sehen Sie sich Abbildung 1 an.

Habe ich jetzt deine Aufmerksamkeit? Ich hoffe doch. Es ist ziemlich beängstigend, sich auf ein System zu verlassen, das uns keine korrekte Mathematik liefert. Aber dieser Artikel wird Ihnen helfen, dies zu vermeiden.

Es gibt einiges zu tun. Wir müssen damit beginnen, worum es bei einem FLOAT-Datentyp geht.

Was ist der SQL-FLOAT-Datentyp?

Der SQL FLOAT-Datentyp ist ein ungefährer numerischer Datentyp, der für Gleitkommazahlen verwendet wird. Sie können sehr große oder sehr kleine Zahlen speichern. Sie werden auch für Berechnungen verwendet, die schnelle Verarbeitungszeiten erfordern.

All dies geht zu Lasten der Genauigkeit. Außerdem können Sie nicht sagen, wo der Dezimalpunkt nach der Berechnung platziert wird – er schwebt . In der Zwischenzeit haben exakte Zahlen wie DECIMAL eine feste Dezimalpunktposition.

Wie Sie einen SQL FLOAT-Datentyp deklarieren

Die Syntax ist FLOAT[(n)], wobei n ist die Anzahl der Bits, die verwendet werden, um die Mantisse einer Gleitkommazahl in wissenschaftlicher Notation zu speichern. Das bestimmt auch die Genauigkeit und die Speichergröße. Die möglichen Werte für n liegen zwischen 1 und 53. Beachten Sie, dass n ist optional.

Hier ist ein Beispiel:

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

Wenn Sie n nicht angeben , der Standardwert ist 53. Das ist auch der Maximalwert. Außerdem ist FLOAT(53) eine Gleitkommazahl mit doppelter Genauigkeit oder binär64. Abgesehen von der Verwendung von FLOAT(53) können Sie es auch als DOUBLE PRECISION deklarieren.

Die folgenden 3 Deklarationen sind funktional gleichwertig:

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

Die Tabelle zeigt die Anzahl der Bits und die entsprechende Speichergröße.

Wert von n Speichergröße
1 bis 24 4 Byte
25 bis 53 8 Byte

Sind SQL FLOAT und REAL gleich?

REAL ist auch FLOAT(24). Es wird auch als Single-Precision oder Binary32 bezeichnet.

Warum es wichtig ist, dies zu wissen

Wenn Sie wissen, dass dies eine ungefähre Zahl ist, werden Sie davon abgehalten, sie für Berechnungen zu verwenden, die Genauigkeit erfordern. Beschäftigen Sie sich auch mit Speicher und Gedächtnis? Verwenden Sie REAL oder FLOAT(24), wenn Sie keine zu großen oder zu kleinen Werte benötigen.

Was sind die Unterschiede zwischen FLOAT und DECIMAL?

FLOAT ist eine ungefähre Zahl. DEZIMAL ist eine exakte Zahl. Hier ist eine Zusammenfassung der Unterschiede in einer Tabelle:

SCHWEBEN DEZIMAL
Dezimalpunkt Kann an beliebiger Stelle in der Ziffer platziert werden Feste Position
Höchstgrenze 38 Ziffern oder 99.999.999.999.999.999.999.999.999.999.999.999.999 FLOAT(53) hat einen maximalen Bereich von 1,79E+308 oder 179 gefolgt von 306 Nullen
Speicherung Maximal 8 Byte Maximal 17 Byte
Rechenergebnis Ungefähr Genau
Vergleichsprüfungen Verwenden Sie nicht =oder <>. Beim Runden vermeiden =oder <> Operatoren. Gut zum Abrunden

Sie haben bereits in Abbildung 1 gesehen, wie die Berechnung einer FLOAT-Zahl zu seltsamen Ergebnissen führen kann. Wenn Sie den Datentyp wie folgt in DECIMAL ändern:

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 

Das Ergebnis wird korrekt sein.
Die Verwendung eines Ungleichheitsoperators ist ebenfalls ein Problem. Schauen Sie sich die Schleife unten an.

DECLARE @floatValue FLOAT(1) = 0.0

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

Was denkst du? Siehe Abbildung 2 unten.

Boom! Endlosschleife! Die Ungleichheitsbedingung wird immer wahr sein. Daher ist es logisch, den Typ in DECIMAL zu ändern.

DECLARE @decimalValue DECIMAL(2,1) = 0.0

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

Der obige Code stoppt sicherlich, wenn @decimalValue ist gleich 5,0. Überzeugen Sie sich selbst in Abbildung 3 unten.

Hübsch! Aber wenn Sie trotzdem auf FLOAT bestehen, funktioniert das auch ohne die Endlosschleife.

DECLARE @floatValue FLOAT(1) = 0.0

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

Inzwischen ist auch das Runden ausgeschaltet. Beachten Sie Folgendes:

DECLARE @value FLOAT(2) = 1.15

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

Statt 1.20 ergibt der Code 1.1. Aber wenn Sie DECIMAL verwenden, wird das Ergebnis korrekt sein.

DECLARE @value DECIMAL(3,2) = 1.15

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

Wenn FLOAT korrekt ist und DECIMAL nicht

Sind exakte Zahlen NICHT immer so genau? Um dieses Problem zu reproduzieren, verwenden wir eine Berechnung und kehren sie dann um. Lassen Sie uns zunächst die Daten vorbereiten.

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)

Die obige Tabelle verwendet feste Werte für die ersten beiden Spalten. Die dritte Spalte enthält die Berechnung. Schließlich führt die vierte, die eine berechnete Spalte ist, die umgekehrte Berechnung durch. Das korrekte Ergebnis in der berechneten Spalte sollte 1 sein.

Um es jetzt mit FLOAT zu vergleichen, erstellen wir eine ähnliche Tabelle und Daten.

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)

Lassen Sie uns fragen.

SELECT * FROM ApproxNumerics1
SELECT * FROM ExactNumerics1

Die Ergebnisse? Sehen Sie sich Abbildung 4 an.

Was ist hier passiert? FLOAT hat es richtig gemacht, aber DECIMAL nicht. Etwas ist schief gelaufen.

IMPLIZITE KONVERTIERUNG ERREICHT ES WIEDER

Die implizite Konvertierung erfolgt, weil SQL nachsichtig ist. Wenn verschiedene Datentypen in einer Berechnung verwendet werden, versucht SQL Server, sie mithilfe einer impliziten Konvertierung hinter unserem Rücken zu konvertieren.

Gab es wirklich eine Bekehrung? Außerdem jede Spalte in ExactNumerics1 Tabelle ist eine DEZIMAL.

Sehen wir uns die Tabellenstruktur von ExactNumerics1 an Tabelle in SQL Server Management Studio:

Beachten Sie den rot umrandeten Bereich in Abbildung 3. Die berechnete Spalte ist DECIMAL(30,17), nicht DECIMAL(8,4). Laut offizieller Dokumentation sind 2 DECIMAL-Spalten mit unterschiedlicher Genauigkeit und Skalierung zwei verschiedene Datentypen . Überzeugen Sie sich hier selbst. Aufgrund der Differenz ist eine Umrechnung erforderlich. Also kommt die implizite Konvertierung ins Spiel.

Was ist, wenn sie unterschiedlich sind und eine implizite Konvertierung stattgefunden hat?

Auch hier kann laut offizieller Dokumentation ein Genauigkeits- oder Skalierungsverlust während der impliziten Konvertierung auftreten . Daher ist ein expliziter CAST erforderlich. Beachten Sie den DECIMAL-Datentyp in der Konvertierungstabelle in dieser Referenz.

Hier ist gerade ein Verlust passiert. Wenn die berechnete Spalte auch DECIMAL(8,4) ist, findet die implizite Konvertierung nicht statt.

Um die implizite Konvertierung zu vermeiden, folgen Sie der offiziellen Dokumentation. Die Tabellenstruktur sollte so aussehen:

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
)

Der explizite CAST in der berechneten Spalte stellt sicher, dass die Datentypen konsistent sind. Wenn wir auch dieser Struktur folgen und die gleichen Daten einfügen, wird das Ergebnis korrekt sein. Sehen Sie sich die neue Ausgabe in Abbildung 6 unten an.

Letztendlich sind exakte Zahlen nicht exakt, wenn eine implizite Konvertierung zwischen 2 oder mehr DECIMAL-Werten erfolgt.

Warum es wichtig ist, dies zu wissen

Es gibt Ihnen eine Vorstellung davon, was Sie für Ihre Tabellen und Variablen benötigen. Darüber hinaus kann die implizite Konvertierung selbst exakte Zahlen verrückt machen. Definieren Sie daher explizit die Genauigkeit und den Maßstab und gehen Sie bei Ihren Berechnungen damit konsistent vor.

Sollte ich SQL FLOAT für Finanzdaten verwenden?

Bei der Berechnung von Prozentsätzen in jedem Segment eines Tortendiagramms sollte die Summe 100 % betragen. Die Summen in den zusammenfassenden und detaillierten Berichten sollten ebenfalls konsistent sein. Wenn die Genauigkeit der Ergebnisse entscheidend ist, reicht ein ungefährer Datentyp wie FLOAT nicht aus. Die logische Wahl dafür ist DECIMAL.

Aber eine Frage bleibt.

Wann sollten Sie FLOAT verwenden?

Verwenden Sie FLOAT für Daten, die astronomische Werte wie Entfernungen zwischen Galaxien erfordern. In der Zwischenzeit erleidet der Datentyp DECIMAL bei diesem Datentyp einen arithmetischen Überlauf. Auch winzige Werte wie der Durchmesser eines Atomkerns passen mit FLOAT. Auch wissenschaftliche Daten und andere Werte, die keine Präzision erfordern, können von FLOAT profitieren.

Warum es wichtig ist, dies zu wissen

Wir sagen nicht, dass FLOAT schlecht und DECIMAL gut ist oder umgekehrt. Wenn Sie die richtigen Anwendungsfälle für jeden kennen, erhalten Sie und Ihre Benutzer die beabsichtigten Ergebnisse. Und andererseits möchten Sie, dass Ihre Benutzer zufrieden sind, richtig?

Schlussfolgerung

Am Ende des Tages wollen wir alle unsere Arbeit machen und gut darin sein. Mathe wird immer Teil unserer Arbeit sein. Und die Kenntnis der richtigen numerischen Datentypen hilft uns auch dabei, damit umzugehen. Es ist nicht schwer, wenn Sie wissen, was Sie tun.

Ich hoffe, dieser Artikel hat Ihnen geholfen, seltsame Mathematik in SQL Server zu vermeiden.

Haben Sie noch etwas hinzuzufügen? Dann lassen Sie es uns im Kommentarbereich wissen. Teilen Sie dies auch auf Ihren bevorzugten Social-Media-Plattformen.