Both of the following queries will give me the same result, and I've seen both techniques being used in the past to ensure a decimal data type is returned.
select CAST(5 as decimal(18,2))/2
select 5*1.0/2
Which method is best to use?
Why would people use *1.0 over casting? Is this just because its quicker to type?
If you want to control the precision or scale of the result, then you'll have to use CAST or CONVERT. But I believe that if you just want "a decimal", then as you suggested, it's just quicker to type * 1.0.
Related
Pinal Dave posted a query that we can use to find the most polluting implicit conversion. The query works very well and in fact I found a very polluting one:
SUM(-[Quantité] * [Conditionnement]) AS QteVendueUQB,
This horror is:
Taking [Quantité] which has data type MONEY
Taking [Conditionnement] which has data type SMALLINT
Multiplying them together
The table looks like this:
Is there a better way to multiply these two values?
As #Larnu wrote in the comment:
If the data types are different, then one of them will be implicitly
converted using data type precedence. There is no way round that. It
is by design, and intended. If you don't want implicit conversion,
don't use different data types for data that will interact with each
other.
It may just be me, but... Despite the fact that most sql developers may consider cast & convert to be very basic stuff, and that may be true, I find Microsoft's documentation page on CAST & CONVERT to be one of the most hideous, not-intuitively-laid-out, hard to understand things I have ever seen. Much of their documentation is great. Like constantly trying to blend the entire page into a mix of both cast and convert, jumping back and forth in each sentence... rather than dealing with them separately. And who puts the target_type as the first argument? Putting the expression as the first argument would be more intuitive - and follow the other 99% of numerous programming languages' syntax. UH
MS says that I can only convert to 3 data types: (well actually I'm not really sure if this applies to both CAST and CONVERT, since they ARE, in fact, different... But according to the layout of that webpage, it apparently applies equally to both - even though I already know for a fact that it is not true for CAST, which I use much more frequently).
It says: "Is the target data type. This includes xml, bigint, and sql_variant"
Putting aside for the moment the fact that I CAST things as many other datatypes all the time (date, varchar),
My immediate question is: if I can only CONVERT to those data types, then why does this work?
select CONVERT(varchar(200), cast(50 as smallint))
And finally, I'd like to run an INSERT that will be getting a smallint and putting it into a varchar(200) column.
All I'm trying to do is avoid any failures, so maybe I don't really "need" to convert or cast over to varchar, but any commments on
answer on what is my apparent misunderstanding about the CONVERT documentation
or
how to safely convert it to insert to varchar
are welcome. As long as you're not just overly unpleasant, since there are always those MS fans who get hot under the collar at all critiques of MS .. :|
Yes, you can convert from smallint to varchar.
1) answer on what is my apparent misunderstanding about the CONVERT
documentation
This may be product of general lack of understanding on what data types are, how can they be converted from one type to another and equally important; what styles are when it comes to the aesthetic representation of a data type.
CAST is an explicit cast operation with no style options.
CONVERT is also an explicit cast that gives you the ability to specify a style for the output.
The documentation clearly states:
Implicit Conversions
Implicit conversions are those conversions that occur without
specifying either the CAST or CONVERT function. Explicit conversions
are those conversions that require the CAST or CONVERT function to be
specified. The following illustration shows all explicit and implicit
data type conversions that are allowed for SQL Server system-supplied
data types. These include xml, bigint, and sql_variant. There is no
implicit conversion on assignment from the sql_variant data type, but
there is implicit conversion to sql_variant.
For your second question
2) how to safely convert it to insert to varchar
Depending of what you mean by safe. Converting to varchar is the convertion that most likely succeed. But whenever to cast to any toher datatype you are intrinsically changing the very nature of the data and will lose precision when casting to smaller types (or applying styles).
The documentation clearly states:
Truncating and Rounding Results
When you convert character or binary expressions (char, nchar,
nvarchar, varchar, binary, or varbinary) to an expression of a
different data type, data can be truncated, only partially displayed,
or an error is returned because the result is too short to display.
Conversions to char, varchar, nchar, nvarchar, binary, and varbinary
are truncated, except for the conversions shown in the following
table.
in other words, casting is never safe.
Numbers always get silently truncated for me. I would propose:
Option 1
Compare the converted value with the original value.
DECLARE #ORIGINAL DECIMAL(13,2) = -99999999999.99 --
DECLARE #EXPECTED VARCHAR(15) = ''
SELECT #EXPECTED = CONVERT(VARCHAR(15),#ORIGINAL)
IF CONVERT(DECIMAL(13,2),#EXPECTED) != #ORIGINAL SELECT 'Ooops'
Option 2
Make sure that all possible values will fit in target varchar.
Decimal(13,2). Widest number possible will be "-99999999999.99" needs varchar(15):
13 chars for digits
1 char for decimal separator
1 char for minus sign
Smallint stores 2 bytes, from "-32768" to "32767", needs varchar(6):
- 5 chars for digits
- 1 char for minus sign
Not sure if you need chars for thousands separators, or if you can change it via settings.
I was performing some simple financial calculations in SQL Server when I discovered some odd behavior. I was trying to convert a string of numbers to a decimal type. While the string did not contain a decimal point, I knew from my specifications that the last 3 positions in the string were supposed to be behind the decimal point.
My first approach was flawed, but went something like this:
select convert(decimal(11,3),89456123/1000) as TotalUnits
This resulted in 89456.000. Performing the division before the cast resulted in the decimal parts being truncated.
So I moved the division operation outside the cast, like this:
select convert(decimal(11,3),89456123)/1000 as TotalUnits
This resulted in an explosion of positions after the decimal point. It returned 89456.12300000
According to my decimal specification, I wanted 11 digits, with 3 of them behind the decimal point. Now I have 13 total digits, with 8 behind the decimal. What happened?
To get what I want, I guess I have to double cast, like this:
select convert(decimal(11,3), convert(decimal(11,3),89456123)/1000)
which gives 89456.123.
It turns out no matter what I divide by, the resulting decimal point explosion is the same. Is the division converting the datatype into a double or something?
My question is this:
Why is this happening, and is there a more elegant way to compensate for it, instead of double-casting to decimal.
EDIT
I found this similar question on SO, but it looks like they are again double-casting.
SQL server does integer arithmetic, to force it to use numeric, you can multiply it by 1.0
No need of using convert twice. This gives 89456.123 with out double convert.
select convert(decimal(11,3),89456123*1.0/1000) as TotalUnits
Why does convert(decimal(11,3),89456123)/1000 end up with 6 decimal places? The rules demand it. numeric division has rather complicated rules about the resulting type.
When you say 1.0 you end up with a numeric with the least scale factors possible to represent this value:
SELECT SQL_VARIANT_PROPERTY(1.11, 'BaseType')
SELECT SQL_VARIANT_PROPERTY(1.11, 'Precision')
SELECT SQL_VARIANT_PROPERTY(1.11, 'Scale')
SELECT SQL_VARIANT_PROPERTY(1.11, 'TotalBytes')
What should you do? I think there is no really elegant solution because of the complicated rules. Any solution I can think of involves rather crazy type inference of intermediate results. I recommend pretty much the same solution that RADAR already gave:
select convert(decimal(11,3), convert(decimal(11, 3), 89456123)/1000) as TotalUnits
The main difference is that I think the *1.0 "trick" used as a short hand for a cast is obfuscating the meaning of the code. If you happen to like it feel free to use it, though.
select convert(decimal(11,3),89456123/CONVERT(decimal(11,3),1000))
I'm facing an issue with the exponential function in postgresql. If I use this select statement select exp(5999), I'm getting:
ERROR: value out of range: overflow
SQL state: 22003
If I use this select statement select exp(5999.1), I'm getting the exponential result.
In other case if I use this statement select exp(9999.1), I'm getting the following error:
ERROR: argument for function "exp" too big
SQL state: 22003
Please let me know why this issue happening and what is the solution for this kind of issue?
I think your first problem is caused by the fact that the output type of exp() is the same as the input type. Because you're using an integral value, it's complaining that the result won't fit in an integer.
That's likely why exp(5999.1), and probably exp(5999.0) works, the floating point type has a larger range.
Your second error is slightly different, it's complaining not of overflow during the calculation, but of the fact the input argument is too large. It's possible that it has some sanity checking on the input somewhere.
Even floating point values run out of range eventually. e9999 is somewhere around 104300, a rather large number and probably well beyond what you'd expect to see in a database application.
In fact, I'd be interested in the use case of such large numbers in a database application. This sounds like something better suited to a bignum package like MPIR.
If you pass an INTEGER argument the exp() function will try to return double precision value. Just above value n=709 it will reach the limit of 64-bit floating point number (about 10^308) and fail to calculate the e^n. The solution is to pass your argument with NUMERIC type:
SELECT EXP(710); -- failure!
SELECT EXP(710::NUMERIC); -- OK
SELECT EXP(5999.1::NUMERIC); -- huge but OK
EDIT!
As for the ERROR: argument for function "exp" too big SQL state: 22003. I've tried to write a work-around. Just run this:
SELECT n, POWER(EXP(1::NUMERIC), n) FROM (VALUES(9998), (9999), (10000)) AS foo (n)
and it will work. But then change 9999 to 9999.1 and you will get that stupid error again. This is ridiculous! 9999.1 is too big but 10000 is fine :D It looks like Postgres doesn't like decimal point in POWER()'s argument. Sorry but I can't fix that.
One solution would be to use the arithmetic property of power and write POWER(POWER(EXP(1::NUMERIC), n*10), 0.1) but that value combination is still too big for Postgres' implementation of power. Good luck with your battle.
I inherited a project that uses SQL Server 200x, wherein a column that stores a value that is always considered as a percentage in the problem domain is stored as its greater than 1 decimal equivalent. For example, 70% (0.7, literally) is stored as 70, 100% as 100, etc. Aside from the need to remember to * 0.01 on retrieved values and * 100 before persisting values, it doesn't seem to be a problem in and of itself. It does make my head explode though... so is there a good reason for it that I'm missing? Are there compelling reasons to fix it, given that there is a fair amount of code written to work with the pseudo-percentages?
There are a few cases where greater than 100% occurs, but I don't see why the value wouldn't just be stored as 1.05, for example, in those cases.
EDIT: Head feeling better, and slightly smarter. Thanks for all the insights.
There are actually four good reasons I can think of that you might want to store—and calculate with—whole-number percentage values rather than floating-point equivalents:
Depending on the data types chosen, the integer value may take up less space.
Depending on the data type, the floating-point value may lose precision (remember that not all languages have a data type equivalent to SQL Server's decimal type).
If the value will be input from or output to the user very frequently, it may be more convenient to keep it in a more user-friendly format (decision between convert when you display and convert when you calculate ... but see the next point).
If the principle values are also integers, then
principle * integerPercentage / 100
which uses all integer arithmetic is usually faster than its floating-point equivalent (likely significantly faster in the case of a floating-point type equivalent to T-SQL's decimal type).
If its a byte field then it takes up less room in the db than floating point numbers, but unless you have millions and millions of records, you'll hardly see a difference.
Since floating-point values can't be compared for equality, an integer may have been used to make the SQL simpler.
For example
(0.3==3*.1)
is usually False.
However
abs( 0.3 - 3*.1 )
Is a tiny number (5.55e-17). But it's pain to have to do everything with (column-SomeValue) BETWEEN -0.0001 AND 0.0001 or ABS(column-SomeValue) < 0.0001. You'd rather do column = SomeValue in your WHERE clause.
Floating point numbers are prone to rounding errors and, therefore, can act "funny" in comparisons. If you always want to deal with it as fixed decimal, you could either choose a decimal type, say decimal(5,2), or do the convert and store as int thing that your db does. I'd probably go the decimal route, even though the int would take up less space.
A good guess is because anything you do with integers (storing, calculating, stuffing into an edit for for a user, etc.) is marginally easier and more efficient than doing the same with floating point numbers. And the rounding issues aren't so obvious when you look at the data.
If these are numbers that end users are likely to see and interact with, percentages are easier to understand than decimals.
This is one of those situations where a notation aid can help; in the program, be consistent in using a prefix (Hungarian) or postfix to specify values that are percentages vs. those that are decimal. If you can extend a naming convention to the database fields themselves, so much the better.
And to add to the data storage issue, if you can use integer arithmetic for whatever processing you are doing, the performance is much better than when doing floating point arithmetic... So storing ther percetages as integer values may allow the processing logic to itilize integer arithmetic
If you're actually using them as a coefficient (or expect users of the database to do this sort of thing in reports), there's a case for storing them as a coefficient - particularly if there's a reason to do calculations involving more than one.
However, if you do this you should be consistent - either all percentages or all coefficients.