Scroll

Division imellem 2 decimaltal

Jeg havde en lidt sjov udfordring på jobbet en dag; jeg lavede en division imellem 2 decimaltal, og jeg kunne slet ikke gennemskue hvorfor jeg fik et helt andet antal decimaler og afrunding end forventet. Det gjorde mig nysgerrig og her er forklaringen.

I store træk så det sådan her ud, mit eksempel er forsimplet til variabler, så er det lettere at illustrere:

DECLARE @decimal1 DECIMAL(38,12) = 1.0,
        @decimal2 DECIMAL(38,12) = 7.45999999

SELECT @decimal1 / @decimal2

Her ville jeg nok normalt forvente 12 decimaler – når vi definerer en decimal type så angiver vi DECIMAL(precision,scale) altså precision 38 = antallet af tal i alt og scale = 12 antallet af decimaler. Resultatet er:

Hvad sker der lige her? 

det vil jeg prøve at forklare. Når SQL Serveren benytte en matematisk operation på 2 datatyper, om det er gange (*) +, – eller dividere (/) 2 decimaler benyttes der en formel for at finde hvilken størrelse resultat decimalen er. Ved division benyttes denne formel:

precision: p1 – s1 + s2 + max(6, s1 + p2 + 1)

scale: max(6, s1 + p2 + 1)

Lad os prøve med et lidt mere simpelt eksempel:

DECLARE @decimal1 DECIMAL(5,3) = 12.345, 
        @decimal2 DECIMAL(5,3) = 54.321

Jeg benytter formlen oven for:

(p = presicion, s = scale)

P1 = 5, S1 = 3

P2 =5, S2 = 3

Det giver følgende formel:

SELECT 5-3+3 + MAX(6, 3+5+1) AS precision,
       MAX(6, 3+5+1) AS scale

Resultatet giver:

precision: 14

scale: 9

Hvilket må betyde at resultat decimalen er en DECIMAL(14,9) lad mig efterprøve den teori:

DECLARE @decimal1 DECIMAL(5,3) = 12.345, 
        @decimal2 DECIMAL(5,3) = 54.321

SELECT @decimal1 / @decimal2
GO

Hvilket giver følgende resultat… tæl gerne decimalerne.

9 decimaler, så her er regnestykket ikke helt ved siden af 😊

Lad mig efterprøve med DECIMAL(38,12)/ DECIMAL(38,12)

Formlen er stadig den samme:

precision: p1 – s1 + s2 + max(6, s1 + p2 + 1)

scale: max(6, s1 + p2 + 1)

P1 = 38, S1 = 12

P2 = 38, S2 = 12

SELECT 38-12+12+(12+38+1) AS precision,
       (12+38+1) AS scale

Det giver Precision 89, Scale 51

Precision kan ikke være højre end 38 og da precision og scale hænger sammen, skal der regnes lidt videre.

Precision: 89 – 38 (38 fordi det er max precision) = 51,

Scale: 51 – (89-38) = 0 … husk formlen scale: max(6, s1 + p2 + 1) så nu har vi max(6,0) hvilket er 6.

Det giver os altså en decimal (38,6) hvilket også er resultatet af:

DECLARE @decimal1 DECIMAL(38,12) = 1.0,
        @decimal2 DECIMAL(38,12) = 7.45999999

SELECT @decimal1 / @decimal2

Hvis du skal bruge flere decimaler end 6 for at få det rigtige resultat, må du ændre lidt på datatyperne, for eksempel som dette:

DECLARE @decimal1 DECIMAL(12,11) = 1.0,
        @decimal2 DECIMAL(12,11) = 7.45999999

SELECT @decimal1 / @decimal2

Decimaltal og præcision

Tænk over hvor mange decimaler og hvor stor præcisions du har behov for når du definerer decimaltal, det kan give unødig afrunding og trunkering af decimaler.

Du kan læse mere på docs her: https://docs.microsoft.com/en-us/previous-versions/sql/sql-server-2005/ms190476(v=sql.90)?redirectedfrom=MSDN

Tak fordi du læste med. Som altid så er du velkommen til at kontakte mig eller en af mine dygtige kollegaer i Unit IT på tlf.: 88 333 333, hvis du har spørgsmål eller udfordringer med dit SQL miljø.

Kontakt os