sql >> Databáze >  >> RDS >> Database

SQL FLOAT:3 body, které vám pomohou vyhnout se podivným matematickým chybám

Napadlo vás někdy, že SQL může být v matematice špatně? Zní to šíleně. Ale pokud jste použili datový typ SQL FLOAT, možná jste narazili na problémy, které se vám chystám ukázat.

Zvaž toto. 0,1 + 0,2 by mělo být 0,3, že? Ale zkontrolujte to pomocí datového typu SQL FLOAT.

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

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

Správný výsledek je 1. Ale zkontrolujte obrázek 1.

Mám teď vaši pozornost? To doufám. Je docela děsivé záviset na systému, který nám nedá správnou matematiku. Ale tento článek vám pomůže se tomu vyhnout.

Je tu nějaká práce. Musíme začít od toho, o čem je datový typ FLOAT.

Co je datový typ SQL FLOAT?

SQL FLOAT datový typ je přibližný číselný datový typ používaný pro čísla s plovoucí desetinnou čárkou. Mohou ukládat velmi velká nebo velmi malá čísla. Používají se také pro výpočty, které vyžadují rychlé zpracování.

To vše přichází za cenu ztráty přesnosti. Dále nemůžete říci, kde bude po výpočtu umístěna desetinná čárka – pluje . Mezitím přesné číslice jako DECIMAL budou mít pevnou pozici desetinné čárky.

Jak deklarujete datový typ SQL FLOAT

Syntaxe je FLOAT[(n)], kde n je počet bitů použitých k uložení mantisy čísla s plovoucí desetinnou čárkou ve vědecké notaci. To také určuje přesnost a velikost úložiště. Možné hodnoty pro n jsou mezi 1 až 53. Všimněte si, že n je volitelné.

Zde je příklad:

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

Pokud nezadáte n , výchozí hodnota je 53. To je také maximální hodnota. Dále FLOAT(53) je číslo s plovoucí desetinnou čárkou s dvojitou přesností nebo binární64. Kromě použití FLOAT(53) jej můžete také deklarovat jako DOUBLE PRECISION.

Následující 3 deklarace jsou funkčně ekvivalentní:

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

Tabulka ukazuje počet bitů a odpovídající velikost úložiště.

Hodnota n Velikost úložiště
1 až 24 4 bajty
25 až 53 8 bajtů

Je SQL FLOAT a REAL totéž?

REAL je také FLOAT(24). Označuje se také jako single-precision nebo binární32.

Proč je důležité toto vědět

Pokud víte, že se jedná o přibližné číslo, nebudete ho používat pro výpočty, které vyžadují přesnost. Zajímá vás také úložiště a paměť? Pokud nepotřebujete příliš velké nebo příliš malé hodnoty, použijte REAL nebo FLOAT(24).

Jaké jsou rozdíly mezi FLOAT a DECIMAL?

FLOAT je přibližné číslo. DECIMAL je přesné číslo. Zde je souhrn rozdílů v tabulce:

Operátory
PLOVÁVÁNÍ DECIMÁLNÍ
Desetinná čárka Lze umístit kdekoli v číslici Pevná pozice
Maximální limit 38 číslic nebo 99,999,999,999,999,999,999,999,999,999,999,999,999 FLOAT(53) má maximální rozsah 1,79E+308 nebo 179 následovaný 306 nulami
Úložiště Maximálně 8 bajtů Maximálně 17 bajtů
Výpočetní výsledek Přibližné Přesně
Kontrola porovnání Nepoužívejte =nebo <>. Vyhněte se zaokrouhlování=nebo <>. Dobré pro zaokrouhlení

Na obrázku 1 jste již viděli, jak může mít výpočet čísla FLOAT podivné výsledky. Pokud změníte typ dat na DECIMAL takto:

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 

Výsledek bude správný.
Problémem je také použití operátoru nerovnosti. Podívejte se na smyčku níže.

DECLARE @floatValue FLOAT(1) = 0.0

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

Co myslíš? Viz obrázek 2 níže.

Výložník! Nekonečná smyčka! Podmínka nerovnosti bude vždy pravdivá. Logickou volbou je tedy změnit typ na DECIMAL.

DECLARE @decimalValue DECIMAL(2,1) = 0.0

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

Výše uvedený kód se určitě zastaví, když @decimalValue se rovná 5,0. Přesvědčte se sami na obrázku 3 níže.

Pěkný! Ale pokud stále trváte na FLOAT, bude to fungovat dobře bez nekonečné smyčky.

DECLARE @floatValue FLOAT(1) = 0.0

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

Mezitím je také vypnuto zaokrouhlování. Zvažte následující:

DECLARE @value FLOAT(2) = 1.15

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

Místo 1.20 je výsledkem kódu 1.1. Ale pokud použijete DECIMAL, výsledek bude správný.

DECLARE @value DECIMAL(3,2) = 1.15

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

Když je FLOAT správně a DECIMAL není

Nejsou přesná čísla vždy tak přesná? K reprodukci tohoto problému použijeme výpočet a poté jej obrátíme. Nejprve si připravme 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)

Výše uvedená tabulka použije pevné hodnoty pro první 2 sloupce. Ve třetím sloupci bude výpočet. Nakonec čtvrtý, což je počítaný sloupec, provede opačný výpočet. Správný výsledek ve vypočítaném sloupci by měl být 1.

Nyní, abychom to porovnali s FLOAT, vytvořte podobnou tabulku a 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)

Pojďme se zeptat.

SELECT * FROM ApproxNumerics1
SELECT * FROM ExactNumerics1

Výsledky? Podívejte se na obrázek 4.

Co se tu stalo? FLOAT to pochopil správně, ale DECIMAL ne. Něco se pokazilo.

IMPLICITNÍ KONVERZE TO ZNOVU DĚLÁ

K implicitní konverzi dochází, protože SQL je shovívavý. Když jsou ve výpočtu použity různé typy dat, SQL Server se je pokusí převést pomocí implicitní konverze za našimi zády.

Opravdu došlo ke konverzi? Kromě toho každý sloupec v ExactNumerics1 tabulka je DESETINNÁ.

Podívejme se na strukturu tabulky ExactNumerics1 tabulka v SQL Server Management Studio:

Všimněte si oblasti s červeným rámečkem na obrázku 3. Vypočítaný sloupec je DECIMAL(30,17), nikoli DECIMAL(8,4). Podle oficiální dokumentace 2 DECIMÁLNÍ sloupce s různou přesností a měřítkem jsou 2 různé datové typy . Přesvědčte se sami zde. Kvůli rozdílu je nutná konverze. Do hry tedy vstupuje implicitní konverze.

Co když se liší a došlo k implicitní konverzi?

Opět platí, že na základě oficiální dokumentace může během implicitní konverze dojít ke ztrátě přesnosti nebo měřítka . Je tedy vyžadován explicitní CAST. Všimněte si datového typu DECIMAL v převodní tabulce v tomto odkazu.

Tady se právě stala nějaká ztráta. Pokud je vypočítaný sloupec také DECIMAL(8,4), implicitní převod nenastane.

Chcete-li se vyhnout implicitní konverzi, postupujte podle oficiální dokumentace. Struktura tabulky by měla být tato:

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
)

Explicitní CAST ve vypočítaném sloupci zajišťuje, že datové typy jsou konzistentní. Pokud také dodržíme tuto strukturu a vložíme stejná data, bude výsledek správný. Podívejte se na nový výstup na obrázku 6 níže.

Přesná čísla nakonec nebudou přesná, pokud k implicitnímu převodu dojde mezi 2 nebo více DECIMAL hodnotami.

Proč je důležité toto vědět

Poskytuje vám představu o tom, co potřebujete pro své tabulky a proměnné. Navíc implicitní převod může způsobit, že i přesná čísla se zblázní. Takže explicitně definujte přesnost a měřítko a buďte s nimi ve svých výpočtech konzistentní.

Mám používat SQL FLOAT pro finanční data?

Při výpočtu procent v každé části koláčového grafu by součet měl být 100 %. Součty v souhrnných a podrobných zprávách by také měly být konzistentní. Pokud je přesnost výsledků klíčová, přibližný datový typ, jako je FLOAT, tuto práci nesplní. Logická volba je DECIMAL.

Ale zůstává otázka.

Kdy byste měli používat FLOAT?

Použijte FLOAT pro data, která vyžadují astronomické hodnoty, jako jsou vzdálenosti mezi galaxiemi. Mezitím bude datový typ DECIMAL trpět aritmetickým přetečením tohoto typu dat. Drobné hodnoty, jako je průměr atomového jádra, se také hodí pomocí FLOAT. Z FLOAT mohou těžit i vědecká data a další hodnoty, které nevyžadují přesnost.

Proč je důležité toto vědět

Neříkáme, že FLOAT je špatný a DECIMAL je dobrý nebo naopak. Znalost správných případů použití pro každý z nich vám a vašim uživatelům poskytne zamýšlené výsledky. A pak znovu, chcete, aby byli vaši uživatelé šťastní, že?

Závěr

Na konci dne chceme všichni dělat svou práci a být v ní dobří. Matematika bude vždy součástí naší práce. A znalost správných číselných datových typů nám také pomůže se s tím vypořádat. Není to těžké, pokud víte, co děláte.

Doufám, že vám tento článek pomohl vyhnout se podivné matematice na serveru SQL.

Chceš ještě něco dodat? Pak nám dejte vědět v sekci Komentáře. Sdílejte to také na svých oblíbených platformách sociálních médií.


  1. Pochopení aliasů sloupců ve výběrovém dotazu na serveru SQL - SQL Server / výukový program TSQL, část 115

  2. Použití dotazu MySQL k procházení řádků k vytvoření rekurzivního stromu

  3. Odstraňování problémů s docházejícími pracovními vlákny

  4. Dynamické maskování dat založené na proxy v FieldShield