Мислили ли сте някога, че SQL може да греши в математиката? Звучи налудничаво. Но ако сте използвали типа данни SQL FLOAT, може да сте се натъкнали на проблемите, които ще ви покажа.
Помислете за това. 0,1 + 0,2 трябва да е 0,3, нали? Но проверете това, като използвате SQL FLOAT тип данни.
DECLARE @f1 FLOAT = 0.1
DECLARE @f2 FLOAT = 0.2
SELECT CASE WHEN @f1 + @f2 = .3 THEN 1 ELSE 0 END
Правилният резултат е 1. Но проверете фигура 1.
Имам ли вниманието ти сега? Сигурно се надявам. Доста е страшно да зависим от система, която няма да ни даде правилна математика. Но тази статия ще ви помогне да избегнете това.
Има работа за вършене. Трябва да започнем от това какво представлява типът данни FLOAT.
Какво е SQL FLOAT тип данни?
SQL FLOAT тип данни е приблизителен числов тип данни, използван за числа с плаваща запетая. Те могат да съхраняват много големи или много малки числа. Те се използват и за изчисления, които изискват бързо време за обработка.
Всичко това идва с цената на загубата на прецизност. Освен това не можете да кажете къде ще бъде поставена десетичната запетая след изчислението - тя плува . Междувременно точните числа като DECIMAL ще имат фиксирана позиция след десетичната запетая.
Как декларирате SQL FLOAT тип данни
Синтаксисът е FLOAT[(n)], където n е броят на битовете, използвани за съхраняване на мантисата на число с плаваща запетая в научна нотация. Това също диктува прецизността и размера на съхранение. Възможните стойности за n са между 1 и 53. Обърнете внимание, че n е по избор.
Ето един пример:
DECLARE @floatValue1 FLOAT; -- Float variable without the number of bits
DECLARE @floatValue2 FLOAT(3) -- Float variable with 3 bits
Ако не посочите n , по подразбиране е 53. Това също е максималната стойност. Освен това FLOAT(53) е число с плаваща запетая с двойна точност или двоично64. Освен да използвате FLOAT(53), можете също да го декларирате като ДВОЙНА ПРЕЦИЗНОСТ.
Следните 3 декларации са функционално еквивалентни:
DECLARE @double1 FLOAT(53);
DECLARE @double2 FLOAT;
DECLARE @double3 DOUBLE PRECISION;
Таблицата показва броя на битовете и съответния размер за съхранение.
Стойност на n | Размер за съхранение |
1 до 24 | 4 байта |
25 до 53 | 8 байта |
Едни и същи ли са SQL FLOAT и REAL?
REAL също е FLOAT(24). Нарича се още като единична точност или двоичен32.
Защо да знаете това е важно
Знаейки, че това е приблизително число, ще ви спре да го използвате за изчисления, които изискват точност. Загрижени ли сте и за съхранението и паметта? Използвайте REAL или FLOAT(24), ако не се нуждаете от твърде големи или твърде малки стойности.
Какви са разликите между FLOAT и DECIMAL?
FLOAT е приблизително число. DECIMAL е точно число. Ето обобщение на разликите в таблица:
ПЛАВАНЕ | DECIMAL | |
десетична запетая | Може да се постави навсякъде в цифрата | Фиксирана позиция |
Максимален лимит | 38 цифри или 99,999,999,999,999,999,999,999,999,999,999,999,999 | FLOAT(53) има максимален обхват от 1,79E+308 или 179, последван от 306 нули |
Съхранение | Максимум 8 байта | Максимум 17 байта |
Резултат от изчисление | Приблизително | Точно |
Проверки за сравнение | Не използвайте =или <>. Избягвайте при закръгляване | =или <> оператори. Добър за закръгляване |
Вече видяхте на фигура 1 как изчисляването на число FLOAT може да има странни резултати. Ако промените типа данни на DECIMAL по следния начин:
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
Резултатът ще бъде правилен.
Използването на оператор на неравенство също е проблем. Вижте цикъла по-долу.
DECLARE @floatValue FLOAT(1) = 0.0
WHILE @floatValue <> 5.0
BEGIN
PRINT @floatValue;
SET @floatValue += 0.1;
END
Какво мислиш? Вижте фигура 2 по-долу.
Бум! Безкраен цикъл! Условието за неравенство винаги ще е вярно. Така че логичният избор е да промените типа на DECIMAL.
DECLARE @decimalValue DECIMAL(2,1) = 0.0
WHILE @decimalValue <> 5.0
BEGIN
PRINT @decimalValue;
SET @decimalValue += 0.1;
END
Горният код със сигурност ще спре, когато @decimalValue е равно на 5.0. Вижте сами на фигура 3 по-долу.
Хубаво! Но ако все пак настоявате за FLOAT, това ще работи добре без безкрайния цикъл.
DECLARE @floatValue FLOAT(1) = 0.0
WHILE @floatValue < 5.0
BEGIN
PRINT @floatValue;
SET @floatValue += 0.1;
END
Междувременно закръгляването също е изключено. Помислете за следното:
DECLARE @value FLOAT(2) = 1.15
SELECT ROUND(@value, 1) -- This will result to 1.1
Вместо 1.20, кодът води до 1.1. Но ако използвате DECIMAL, резултатът ще бъде правилен.
DECLARE @value DECIMAL(3,2) = 1.15
SELECT ROUND(@value, 1) -- This will result in 1.2 or 1.20
Когато FLOAT е правилно, а DECIMAL не е
Точните цифри НЕ СА ли толкова точни през цялото време? За да възпроизведем този проблем, ще използваме изчисление и след това ще го обърнем. Първо, нека подготвим данните.
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)
Таблицата по-горе ще използва фиксирани стойности за първите 2 колони. Третата колона ще съдържа изчислението. И накрая, четвъртата, която е изчислена колона, ще извърши обратното изчисление. Правилният резултат в изчислената колона трябва да бъде 1.
Сега, за да го сравним с FLOAT, нека създадем подобна таблица и данни.
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)
Нека да запитаме.
SELECT * FROM ApproxNumerics1
SELECT * FROM ExactNumerics1
Резултатите? Вижте фигура 4.
Какво се е случило тук? FLOAT разбра правилно, но DECIMAL не. Нещо се обърка.
НЕПРЕКРАСНОТО КОНВЕРСИРАНЕ ГО ПРАВЯ ОТНОВО
Неявното преобразуване се случва, защото SQL прощава. Когато в изчисление се използват различни типове данни, SQL Server се опитва да ги преобразува, използвайки имплицитно преобразуване зад гърба ни.
Наистина ли се е случило преобразуване? Освен това всяка колона в ExactNumerics1 таблицата е DECIMAL.
Нека проверим структурата на таблицата на ExactNumerics1 таблица в SQL Server Management Studio:
Обърнете внимание на червеното поле на фигура 3. Изчислената колона е DECIMAL(30,17), а не DECIMAL(8,4). Според официалната документация, 2 DECIMAL колони с различна точност и мащаб са 2 различни типа данни . Вижте сами тук. Поради разликата се изисква преобразуване. И така, имплицитното преобразуване влиза в игра.
Ами ако са различни и се е случило имплицитно преобразуване?
Отново, въз основа на официалната документация, може да се случи загуба на прецизност или мащаб по време на имплицитно преобразуване . Следователно се изисква изрично CAST. Обърнете внимание на типа данни DECIMAL в таблицата за преобразуване в тази справка.
Тук току-що се случи някаква загуба. Ако изчислената колона също е DECIMAL(8,4), имплицитното преобразуване не се осъществява.
За да избегнете имплицитното преобразуване, следвайте официалната документация. Структурата на таблицата трябваше да бъде следната:
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
)
Изричното CAST в изчислената колона гарантира, че типовете данни са последователни. Ако също следваме тази структура и вмъкнем същите данни, резултатът ще бъде правилен. Вижте новия изход на фигура 6 по-долу.
В крайна сметка точните числа няма да са точни, ако се случи имплицитно преобразуване между 2 или повече DECIMAL стойности.
Защо да знаете това е важно
Дава ви представа какво ви трябва за вашите таблици и променливи. Нещо повече, имплицитното преобразуване може да доведе до безумие дори точните числа. Така че, дефинирайте изрично точността и мащаба и бъдете последователни с тях в изчисленията си.
Трябва ли да използвам SQL FLOAT за финансови данни?
Когато се изчисляват процентите във всеки отрязък от кръгова графика, сумата трябва да бъде 100%. Общите суми в обобщените и подробните отчети също трябва да бъдат последователни. Ако точността на резултатите е от решаващо значение, приблизителен тип данни като FLOAT няма да свърши работа. Логичният избор за това е DECIMAL.
Но остава въпросът.
Кога трябва да използвате FLOAT?
Използвайте FLOAT за данни, които изискват астрономически стойности, като разстояния между галактиките. Междувременно типът данни DECIMAL ще претърпи аритметично препълване с този тип данни. Малки стойности като диаметъра на атомно ядро също ще се поберат с помощта на FLOAT. Научните данни и други стойности, които не изискват прецизност, също могат да се възползват от FLOAT.
Защо да знаете това е важно
Ние не казваме, че FLOAT е лош, а DECIMAL е добър или обратното. Познаването на правилните случаи на употреба за всеки ще даде на вас и вашите потребители очакваните резултати. И отново, искате вашите потребители да са доволни, нали?
Заключение
В края на деня всички искаме да вършим работата си и да бъдем добри в нея. Математиката винаги ще бъде част от нашата работа. И познаването на правилните числови типове данни също ще ни помогне да се справим с него. Не е трудно, ако знаеш какво правиш.
Надявам се тази статия да ви е помогнала да избегнете странна математика в SQL Server.
Имате ли още нещо да добавите? След това ни уведомете в секцията за коментари. Споделете това и в любимите си социални медийни платформи.