We have precision stored in a database table. We want to round the amount for each currency as per its own precision. In Mysql this can be done by using FORMAT but I am unable to do this in Snowflake.
e.g in MYSQL FORMAT(2.34, table.precision) returns 2.340 if the precision is 3 and 2.34 if the precision is 2.
Can this be done in Snowflake db ?
Thanks
SELECT
2.34124544 AS f
,round(2.34124544, r)
,'99.'|| RPAD('', r, '9') AS form
,TO_CHAR(f,form)
FROM VALUES (0),(1),(2),(3) v(r);
gives:
F ROUND(2.34124544, R) FORM TO_CHAR(F,FORM)
2.34124544 2.00000000 99. 2.
2.34124544 2.30000000 99.9 2.3
2.34124544 2.34000000 99.99 2.34
2.34124544 2.34100000 99.999 2.341
So you can round to the precision you want, or build a format string and format the out also. and how it's working.
Thus compacted up:
SELECTTO_CHAR(2.34124544, '99.'|| RPAD('', r, '9') )
FROM VALUES (0),(1),(2),(3) v(r);
assuming you want to format it for output, your best call would be:
https://docs.snowflake.com/en/sql-reference/functions/to_char.html - this allows entering a format string
the very last example in the doc demonstrates the use.
Related
I have a huge table of product rows... I only need a small portion of its data, more specifically the prices of the products (regular price - for which I have to choose between two fields in the sense that if one is present, I pick it, otherwise I pick the other; and sale price - which for many products is stored as a float with three decimals, because it was calculated as a percentage of the regular price). So I crafted the appropriate query to achieve what I want, and noticed a very strange behavior for the ROUND() function.
In some cases, when the third decimal digit is 5 (ie. .165) is truncated to .16 and in others it's rounded up to .17, and this happens for any other number with 5 at the third decimal place as well of course! How can that be possible? Here is the query:
SELECT CODE, FWHSPRICE, RTLPRICE, CASE WHEN ISNULL(FWHSPRICE, 0) = 0 THEN RTLPRICE ELSE FWHSPRICE END AS REGULAR, ROUND(FLDFLOAT3, 2) AS SALE
FROM MATERIAL
WHERE COMID = 12
AND FLTID1 = 1
And here is a screenshot of a comparison between the two recordsets, on the left without ROUND() in the query, and on the right with ROUND()
PS: If you want me to export data for replication, can you please explain to me how to create the appropriate INSERT statements for you? The whole table has so many fields - and rows, and I don't know how to set SSMS to do that. I'm coming from MySQL, so this "realm" of SQL Server is so new to me... Thank you in advance.
Yeah, you're mixing two things that have their own sets of quirky behavior (IMHO). I would honestly just not use float unless I needed the specific properties of float, but if you're stuck with this data type...
I would first convert from float to decimal with an extra decimal place (or maybe even 2), then use another convert to round instead of round itself. For example:
DECLARE #x TABLE(x float);
INSERT #x(x) VALUES(0.615),(0.165),(0.415),(0.414);
SELECT
x,
bad = ROUND(x, 2),
better = CONVERT(decimal(10,2), CONVERT(decimal(10,3), x))
FROM #x;
Results:
x
bad
better
0.615
0.61
0.62
0.165
0.17
0.17
0.415
0.41
0.42
0.414
0.41
0.41
Example db<>fiddle
If you have values like 0.4149, you can see how an extra decimal place will prevent that from rounding up (unless that's the behavior you want):
DECLARE #f float = 0.4149;
SELECT source = #f,
round_up = CONVERT(decimal(10,2), CONVERT(decimal(10,3), #f)),
round_down = CONVERT(decimal(10,2), CONVERT(decimal(10,4), #f));
Results:
source
round_up
round_down
0.4149
0.42
0.41
I'm trying to convert duration in one column which is written in milliseconds (Ex: 600,2101,1110....) to hh:mm:ss.000 format(Ex:00:00:00.600, 00:00:02.101...) using the below formula in google spreadsheets:
=CONCATENATE(TEXT(INT(A1/1000)/86400,"hh:mm:ss"),".",A1-(INT(A1/1000)*1000))
It gives correct values for almost all , but one type of values which is durations having '0' as their second digit (Eg: 2010,3056,1011).
When 0 is the second digit , the after decimal value in hh:mm:ss.000 is rounded to the third digit and 0 is ignored (Example row 1 and 2 in below table). But for other durations it gives right value(row 3).
I need a formula that works well on all type of values i.e 1080 → 00:00:01.080 and not 00:00:01.80 .
Can someone please help with this.
Duration in milliseconds
hh:mm:ss.000 format
1080
00:00:01.80 (wrong)
2010
00:00:02.10 (wrong)
1630
00:00:01.630 (correct)
try:
=INDEX(IF(A2:A="",,TEXT(A2:A/86400000, "hh:mm:ss.000")))
I have a float variable-field1 in 2 tables-table1 & table2. When I query the table and check the values of the field both look identical but when I find their difference it gives a difference instead of zero.
Field1(Table1) value---84.4660194174757
Field2(Table2) value---84.4660194174757
Differnce---1.4210854715202E-14
Why would I get this problem?
Use ROUND to limit the decimal places
Use ABS(value1-value2) < 0.00001 with some suitable value
Don't use float
It seems that SQL Server does not accept numbers formatted using any particular locale.
It also doesn't support locales that have digits other than 0-9.
For example, if the current locale is bengali, then the number 123456789 would come out as "১২৩৪৫৬৭৮৯". And that's just the digits, nevermind what the digit grouping would be.
But the same problem happens for numbers in the Invariant locale, which formats numbers as "123,456,789", which SQL Server won't accept.
Is there a culture that matches what SQL Server accepts for numeric values? Or will i have to create some custom "sql server" culture, generating rules for that culture myself from lower level formatting routines?
If i was in .NET (which i'm not), i could peruse the Standard Numeric Format strings. Of the format codes available in .NET:
c (Currency): $123.46
d (Decimal): 1234
e (Exponentional): 1.052033E+003
f (Fixed Point): 1234.57
g (General): 123.456
n (Number): 1,234.57
p (Percent): 100.00 %
r (Round Trip): 123456789.12345678
x (Hexadecimal): FF
Only 6 accept all numeric types:
c (Currency): $123.46
d (Decimal): 1234
e (Exponentional): 1.052033E+003
f (Fixed Point): 1234.57
g (General): 123.456
n (Number): 1,234.57
p (Percent): 100.00 %
r (Round Trip): 123456789.12345678
x (Hexadecimal): FF
And of those only 2 generate string representations, in the en-US locale anyway, that would be accepted by SQL Server:
c (Currency): $123.46
d (Decimal): 1234
e (Exponentional): 1.052033E+003
f (Fixed Point): 1234.57
g (General): 123.456
n (Number): 1,234.57
p (Percent): 100.00 %
r (Round Trip): 123456789.12345678
x (Hexadecimal): FF
Of the remaining two, fixed is dependant on the locale's digits, rather than the number being used, leaving General g format:
c (Currency): $123.46
d (Decimal): 1234
e (Exponentional): 1.052033E+003
f (Fixed Point): 1234.57
g (General): 123.456
n (Number): 1,234.57
p (Percent): 100.00 %
r (Round Trip): 123456789.12345678
x (Hexadecimal): FF
And i can't even say for certain that the g format won't add digit groupings (e.g. 1,234).
Is there a locale that formats numbers in the way SQL Server expects? Is there a .NET format code? A java format code? A Delphi format code? A VB format code? A stdio format code?
latin-numeral-digits
The SQL Server specifications for constants are describing the acceptable formats in T-SQL expressions and batches:
integer constants are represented by a string of numbers that are not
enclosed in quotation marks and do not
contain decimal points. Integer
constants must be whole numbers; they
cannot contain decimals.
decimal constants are represented by a string of numbers that are not
enclosed in quotation marks and
contain a decimal point.
float and real constants are represented by using scientific
notation.
money constants are represented as string of numbers with an optional
decimal point and an optional currency
symbol as a prefix. Money constants are
not enclosed in quotation marks. SQL
Server does not enforce any kind of
grouping rules such as inserting a
comma (,) every three digits in
strings that represent money. Commas
are ignored anywhere in the specified
money literal.
To indicate whether a number is positive or negative, apply the + or - unary operators to a numeric constant. This creates a numeric expression that represents the signed numeric value. Numeric constants use positive when the + or - unary operators are not applied.
The good news is that client applications don't need to worry about these requirements. Client applications should pass numeric values as #parameters, not as T-SQL literal constants.
I have a list of values such as "12000","12345","123456" that need to be converted to currency ("120.00", "123.45", "1234.56"). The only way I know is to convert the value to a string, copy the first strlen()-2 characters to one string (dollars) and the remainging two digits to another string(cents) and then write them as the following:
printf("%s.%s", dollars, cents);
printf("$%.2f", value/100);
Don't use floats for storing or representing monetary amounts. Use longs (if you need more than 4 billion cent use llongs). Its usually a good idea to represent currency in its minimum usable unit, example use 10000 to represent 100Euro). Then the correct way to format these values (assuming 100 cent to the euro or dollar) is:
printf( "%d.%02d", value/100, value%100);
Hope that makes sense...
Calculations with currency values is a complex subject but you cant go far wrong is you always aim to have a rounded answer to the nearest currency unit (cent for example) and always make sure that rounding errors are calculated for (example, to divide 1 dollar three ways you should end up with 33+33+34 or 33+33+33+1).
to prefix values less than $1.00 with 0, use:
printf( "$%0.2f", value / 100.0 );
This will result in $0.25 if value = 25