Login or Sign Up to become a member!
LessThanDot Sit Logo

LessThanDot

Data Management

Less Than Dot is a community of passionate IT professionals and enthusiasts dedicated to sharing technical knowledge, experience, and assistance. Inside you will find reference materials, interesting technical discussions, and expert tips and commentary. Once you register for an account you will have immediate access to the forums and all past articles and commentaries.

LTD Social Sitings

Lessthandot twitter Lessthandot Linkedin Lessthandot friendfeed Lessthandot facebook Lessthandot rss

Note: Watch for social icons on posts by your favorite authors to follow their postings on these and other social sites.

Your profile

Authors

Search

XML Feeds

Google Ads

« SQL Server efficient handling of divide by zeroSetting a standard DateFormat for SQL Server »
comments
Rate Post:
submit to reddit Digg!FacebookDotnetkicks

Many people are confused about SQL Server's precision and scale. This is unfortunate because choosing the correct values for precision and scale is critically important when you perform math operations using the decimal/numeric data type. The point of this blog is to explain how SQL Server determines the data type for math operations, and the order in which conversions occur.

For example:

  1. SELECT 10 / 3           UNION All
  2. SELECT 10 / 3.0         UNION All
  3. SELECT 10 / 3.00        UNION All
  4. SELECT 10 / 3.000       UNION All
  5. SELECT 10 / 3.0000      UNION All
  6. SELECT 10 / 3.00000     UNION All
  7. SELECT 10 / 3.000000    UNION All
  8. SELECT 10 / 3.0000000   UNION All
  9. SELECT 10 / 3.00000000

Let's take a close look at the above query so that we can predict the output. Of course, it will help if we know the data types that SQL Server uses. There is a relatively obscure function that you can use to determine the data types. SQL_VARIANT_PROPERTY

For the first calculation, we have 10 / 3. We all know the answer is 3 1/3, but how is this expressed in the output?

Well, using the SQL_VARIANT_PROPERTY function, we can determine the data type that SQL Server will use.

  1. SELECT SQL_VARIANT_PROPERTY(3, 'BaseType') AS [Base Type],
  2.        SQL_VARIANT_PROPERTY(3, 'Precision') AS [PRECISION],
  3.        SQL_VARIANT_PROPERTY(3, 'Scale') AS [Scale],
  4.  
  5.        SQL_VARIANT_PROPERTY(10, 'BaseType') AS [Base Type],
  6.        SQL_VARIANT_PROPERTY(10, 'Precision') AS [PRECISION],
  7.        SQL_VARIANT_PROPERTY(10, 'Scale') AS [Scale]

The output indicates SQL Server will use Integer, Precision 10, scale 0. Using integer math, the output will be 3. Since both values are integer, the result is an integer.

Now, let's look at the next one. 10/3.0

  1. SELECT SQL_VARIANT_PROPERTY(3.0, 'BaseType') AS [Base Type],
  2.        SQL_VARIANT_PROPERTY(3.0, 'Precision') AS [PRECISION],
  3.        SQL_VARIANT_PROPERTY(3.0, 'Scale') AS [Scale],
  4.  
  5.        SQL_VARIANT_PROPERTY(10, 'BaseType') AS [Base Type],
  6.        SQL_VARIANT_PROPERTY(10, 'Precision') AS [PRECISION],
  7.        SQL_VARIANT_PROPERTY(10, 'Scale') AS [Scale]

This time, we get Numeric(2,1) (for 3.0) and int for the 10. There are well defined (although obscure) rules for math operations. Full rules here: Precision, Scale, and Length

For division, the rule is:

Precision = p1 - s1 + s2 + max(6, s1 + p2 + 1)
Scale = max(6, s1 + p2 + 1)

p1 represents the precision of the first number. s1 represents the scale of the first number. P2 and S2 represent the second number.

10 / 3.0
P1 = 10
S1 = 0
P2 = 2
S2 = 1

Precision = p1 - s1 + s2 + max(6, s1 + p2 + 1)
Precision = 10 - 0 + 1 + max(6, 0 + 2 + 1)
Precision = 11 + Max(6, 3)
Precision = 11 + 6
Precision = 17

Unfortunately, this isn't correct because the SQL_VARIANT_PROPERTY is returning 10 for the integer. When dividing the numbers, SQL Server actually converts the integer to a decimal, using the smallest value possible to represent the value. In this case, 10 is converted to decimal(2,0). Performing the calculations again:

10 / 3.0
P1 = 2
S1 = 0
P2 = 2
S2 = 1

Precision = p1 - s1 + s2 + max(6, s1 + p2 + 1)
Precision = 2 - 0 + 1 + max(6, 0 + 2 + 1)
Precision = 3 + Max(6, 3)
Precision = 3 + 6
Precision = 9

Scale = max(6, s1 + p2 + 1)
Scale = max(6, 0 + 2 + 1)
Scale = max(6,3)
Scale = 6

So, Select 10 / 3.0 = 3.333333

Now, let's fast forward to the last calculation. Select 10 / 3.00000000

Precision/scale for the 10 (after converting to decimal) = 2,0
Precision/scale for the 3.00000000 = 9,8

10 / 3.00000000
P1 = 2
S1 = 0
P2 = 9
S2 = 8

Precision = p1 - s1 + s2 + max(6, s1 + p2 + 1)
Precision = 2 - 0 + 8 + max(6, 0 + 9 + 1)
Precision = 10 + Max(6, 10)
Precision = 10 + 10
Precision = 20

Scale = max(6, s1 + p2 + 1)
Scale = max(6, 0 + 9 + 1)
Scale = max(6,10)
Scale = 10

The result is 3.3333333333 Decimal(20,10)

Lastly, since the original example was a UNION ALL query, all results are converted to the same data type. Each result is first calculated, then finally converted to a data type that satisfies all results. In this case, each result is converted to a Decimal(20,10). But remember, this ONLY occurs after each calculation is performed!

Select 10 / 3           3.0000000000 Int
Select 10 / 3.0         3.3333330000 Decimal(9,6)
Select 10 / 3.00        3.3333330000 Decimal(10,6)
Select 10 / 3.000       3.3333330000 Decimal(11,6)
Select 10 / 3.0000      3.3333330000 Decimal(12,6)
Select 10 / 3.00000     3.3333333000 Decimal(14,7)
Select 10 / 3.000000    3.3333333300 Decimal(16,8)
Select 10 / 3.0000000   3.3333333330 Decimal(18,9)
Select 10 / 3.00000000  3.3333333333 Decimal(20,10)

About the Author

George has been developing software professionally for 19 years, first for the department of defense, and then for various other companies. In 1998, George started his software company, Orbit Software, specializing in School Bus Transportation software. His specialty is refining SQL Server queries to deliver optimal performance.
Social SitingsTwitterLTD RSS Feed
4254 views
submit to reddit Digg!FacebookDotnetkicks

Comments and Feedback

5 comments

Comment from: SQLDenis [Member] Email
*****
Very nice post George
I am still amazed at the number of people who get bitten by integer math these days
24/11/08 @ 09:04
Comment from: kaht [Member] Email
*****
Interesting.
24/11/08 @ 09:22
Comment from: Joe Celko [Visitor] Email · http://www.celko.com
*****
To make thing s worse, scale and precision are implementation defined in the SQL Standards. This makes porting code and data harder than we might like it to be. Only COBOL seems to have spent poem time on rules for this stuff ...
29/11/08 @ 14:07
Comment from: Albert Lumu [Visitor] · http://myc4.com
*****
Man, I came across this after fighting a precision bug for the last 24hrs+

Thanks!
13/01/09 @ 07:14
Comment from: George Mastros [Member] Email
Albert Lumu,

I'm glad this was helpful for you.

I can't imagine fighting a bug for 24 hours. The next time this happens, I would encourage you to seek help in the Less Than Dot forum, here:

http://forum.lessthandot.com/viewforum.php?f=17

13/01/09 @ 07:21

Leave a comment


Your email address will not be revealed on this site.

Your URL will be displayed.
(Line breaks become <br />)
(Name, email & website)
(Allow users to contact you through a message form (your email will not be revealed.)