views:

136

answers:

7

Hi there I am wondering whats the best "price field" in SQL Server for a shoplike structure?

Looking at this overview: http://www.teratrax.com/sql_guide/data_types/sql_server_data_types.html

We have data types called money, smallmoney, then we have decimal/numeric and lastly float and real

Name, memory/disk-usage and value ranges:

  • Money: 8 bytes (values: -922,337,203,685,477.5808 to +922,337,203,685,477.5807)
  • Smallmoney: 4 bytes (values: -214,748.3648 to +214,748.3647)
  • Decimal: 9 [default, min. 5] bytes (values: -10^38 +1 to 10^38 -1 )
  • Float: 8 bytes (values: -1.79E+308 to 1.79E+308 )
  • Real: 4 bytes (values: -3.40E+38 to 3.40E+38 )

My question is: is it really wise to store price values in those types?

what about eg. INT?

  • Int: 4 bytes (values: -2,147,483,648 to 2,147,483,647)

Lets say a shop uses dollars, they have cents, but I don't see prices being $49.2142342 so the use of a lot of decimals showing cents seems waste of SQL bandwidth. Secondly, most shops wouldn't show any prices near 200.000.000 (not in normal webshops at least... unless someone is trying to sell me a famous tower in Paris)

So why not go for an int?

An int is fast, its only 4 bytes and you can easily make decimals, by saving values in cents instead of dollars and then divide when you present the values.

The other approach would be to use smallmoney which is 4 bytes too, but this will require the math part of the CPU to do the calc, where as Int is integer power... on the downside you will need to divide every single outcome.

Are there any "currency" related problems with regional settings when using smallmoney/money fields? what will these transfer too in C#/.NET ?

Any pros/cons? Go for integer prices or smallmoney or some other?

What does your experience tell?

+5  A: 

Use the Money datatype if you are storing money (unless modelling huge amounts of money like the national debt) - it avoids precision/rounding issues.

The Many Benefits of Money…Data Type!

Mitch Wheat
And since you are using C#, go for the decimal datatype when handling money in your C# code. This will avoid rounding issues you would normally have with float/double datatypes.
Kevin
@Kevin As far as I have read the Decimal type can be up to 20 times more slow than using Double and Money, because of its precision. I am not sure its the best choice for everything.
BerggreenDK
Nothing is the best choice for everything. You are dealing with money. You prob. don't want rounding errors.For example, if you add 10 x 10 cents to a float, what will you get? Technically, 0.1 * 10 = 1. However, if you look at the sum of those 0.1s in the debugger, it is "1.00000012". If you want to compare that to 1, you can't unless you use an epsilon value. http://docs.sun.com/source/806-3568/ncg_goldberg.htmlI doubt the speed penalty is a problem at all, unless you are doing something very very intensive. It is still very fast. Premature optimization is the root of all evil after all.
Kevin
@Kevin "Premature optimization is the root of all evil" - I get the point, but thinking about the right datastructure from the start is not premature optimzation in my opinion. Its about building a strong foundation below the surface. I think its okay to evaluate pro/cons before implementing the same type all over a giant SQL databases + tons of classes. My question was also more a discussion of which is best practice for which situations. So thanks for your opinions I really appriciate your time.
BerggreenDK
+2  A: 

Personally, I'd use smallmoney or money to store shop prices.

Using int adds complexity elsewhere.

And 200 million is perfectly valid price in Korean Won or Indonesian Rupees too...

gbn
good point, didnt think of such currencies! great!
BerggreenDK
+3  A: 

If you're absolutely sure your numbers will always stay within the range of smallmoney, use that and you can save a few bytes. Otherwise, I would use money. But remember, storage is cheap these days. The extra 4 bytes over 100 million records is still less than half a GB. As @marc_s points out, however, using smallmoney if you can will reduce the memory footprint of SQL server.

Long story short, if you can get away with smallmoney, do. If you think you might go over the max, use money.

But, do not use a floating-decimal type or you will get rounding issues and will start losing or gaining random cents, unless you deal with them properly.

My argument against using int: Why reinvent the wheel by storing an int and then having to remember to divide by 100 (10000) to retrieve the value and multiply back when you go to store the value. My understanding is the money types use an int or long as the underlying storage type anyway.

As far as the corresponding data type in .NET, it will be decimal (which will also avoid rounding issues in your C# code).

lc
I agree on reinventing the wheel. The concern was memory usage vs cpu power for large transactions of joins on joins on joins etc. Lets say millions of rows. But I think "money" or "smallmoney" is the conclusion after all. Good. :o)
BerggreenDK
@Ic: yes, disk storage is cheap these days - but your choice of data types also has a direct impact on the RAM comsumption of SQL Server - it loads disk pages into RAM 1:1 - no compression, no moving around of bits. So if you waste space on disk - you're also wasting spaec in your server's memory - and that's not quite as cheap as disk space...
marc_s
@marc_s: that was my original thought for this question, but the overhead added if we used int seems even worse, so smallmoney might be the best answer
BerggreenDK
@BerggreenDK: absolutely, I agree - go with SMALLMONEY. I was just trying to point out what lots of folks don't take into account - choosing your data types also affects your server's RAM consumption - not just the disk space needed.
marc_s
@marc_s Valid point and agreed.
lc
Hmmm! Just stumpled upon another discussion regarding Decimal type in C#, its 128 bit (http://stackoverflow.com/questions/618535/what-is-the-difference-between-decimal-float-and-double-in-c) - not great for use with smallmoney I guess
BerggreenDK
@BerggreenDK Interesting to know, but using `smallmoney` will keep the memory use of the *server* down, right? So it's not all in vain.
lc
+3  A: 

SQL data types money and smallmoney both resolve to c# decimal type:

http://msdn.microsoft.com/en-us/library/system.data.sqltypes.sqlmoney(v=VS.71).aspx

So I'm thinking that you might as well go for decimal. Personally I've been using double all my life working in the financial industry and haven't experienced performance issues, etc. Actually, I've found that for certain calculations, etc., having a larger data type allows for higher degree of accuracy.

code4life
A: 

I would go for the Money datatype. Invididually you may not exceed the value in Smallmoney, but it would be easy for multiple items to exceed it.

Craig T
+3  A: 

USE NUMERIC / DECIMAL. Avoid MONEY / SMALLMONEY. Here's an example of why. Sooner or later the MONEY / SMALLMONEY types will likely let you down due to rounding errors. The money types are completely redundant and achieve nothing useful - a currency amount being just another decimal number like any other.

Lastly, the MONEY / SMALLMONEY types are proprietary to Microsoft. NUMERIC / DECIMAL are part of the SQL standard. They are used, recognised and understood by more people and are supported by most DBMSs and other software.

dportas
Well, I dont think we will divide by 10000 so I hope we are safe enough. Secondly, the suggest NUMERIC(19,4) in the test, will use 9 bytes pr. pricetag. Thats quite some "extra" data to be safe of something that I dont expect to happend.The problem with that specific test as I see it, is that you start by dividing the digits off, and then you multiply.If you do the exact oppersite: SET @mon4 = @mon1*@mon3/@mon2then you get same result.I agree, it will demand a little extra of our algoritmes when testing and constructing, but I dont think we will have the problem.
BerggreenDK
MONEY requires 8 bytes and the equivalent DECIMAL requires 9. SMALLMONEY requires 4 bytes and the equivalent DECIMAL requires 5. I wouldn't describe 1 byte as "quite some extra". But perhaps I'm just not understanding why you want to use a MONEY/SMALLMONEY type in the first place. What advantage do you think it gives you? Would you use a MONEY type for a decimal value that wasn't a currency at all and if not then why not?
dportas
@David- Since when is MONEY type proprietary to Microsoft?.. Informix has been using it since 1983!
Frank Computer
@Frank: I didn't know that about Informix. I meant that MONEY is a vendor-specific extension. It isn't part of the SQL Standard.
dportas
A: 

In my pawnshop app, the pawnshop operators lend from $5.00 to $10,000.00 When they calculate the loan amount they round it to the nearest dollar in order to avoid dealing with cents (the same applies for interest payments). When the loan amount is above $50.00 they will round it to the nearest $5.00 (i.e. $50, $55, $60 ...), again to minimize running out of dollar bills. Therefore, I use DECIMAL(7,2) for transaction.calculated_loan_amount and DECIMAL(5,0) for transaction.loan_amount. The app calculates the loan amount to the penny and places that amount in loan_amount where it gets rounded to the nearest dollar when below $50 or to the nearest $5.00 when greater.

Frank Computer