How to Format a String with Leading Zeroes to Decimal in TSQL? - tsql

I'm trying to parse a string such that the amount field is formatted with 2 decimal places. The original data provides 2 decimal place accuracy. However, the amount field in the data file is 16 characters long with leading zeroes. For example:
0000000000407981
should convert to 4079.81.
I've tried the following in my select statement:
format((substring([Column 0],51,16)/100), '.00') as CheckAmount,
This produces an amount with 2 decimal places but rounds to the nearest whole dollar. I'm using SQL Server 2016.
How do I modify my statement to ensure the CheckAmount is formatted with 2 decimal point accuracy and contains an accurate value?
Update: I attempted to convert to integer as follows:
format(convert(int, (substring([Column 0],51,16)/100.0)), '.00') as CheckAmount,
Unfortunately, I receive an error stating:
Arithmetic overflow error converting varchar to data type numeric.
How should I remedy this?

The divide by 100 does an integer division. You do not get decimal numbers. Dividing by 100.0 will.
declare #num nvarchar(20) = '0000000000407981';
select convert(bigint, #num)/100.0;
Fiddle

I was able to solve this issue with the following code snippet:
CAST((CAST(substring([Column 0],51,16) as int)/100.0) as decimal(18,2)) as CheckAmount,
Hope this helps anyone who might need to accomplish a similar task.

Related

how to keep datatype when substracting day from date/time column in SAS

My question is really simple, hope someone deigns to answer!.
Being very to new SAS, the date and its formats really is confusing me.
I have timestamp column from which I need to substract 2 days while keeping its datatype
The value of the column is "2022-04-20-19.37.57.714699"
What I need is "2022-04-18-19.37.57.714699"
When I try this I get number datatype:
PROC SQL;
CREATE TABLE my_table AS
SELECT
cust_id,query_date, (query_date)-2 as calc_date
FROM other_table
;quit;
I try format,datetime function, but ended up with "Statement is not valid or it is used out of proper order"
Thanks
Assuming that the QUERY_DATE variable is numeric and has datetime values in it (the number of seconds since 1960) then you can use the INTNX() function with the DTDAY interval to adjust the value by two days. To keep the same time of day use SAME for the alignment parameter.
intnx('dtday',query_date,-2,'same')
Alternatively you could just subtract 48 hours worth of seconds from the value.
query_date -2*'24:00:00't
If you want the values to display in a human readable way then attach any of the many datetime formats, such as DATETIME to the new variable.
CREATE TABLE my_table AS
SELECT cust_id,query_date
, intnx('dtday',query_date,2,'same') as calc_date format=datetime20.
FROM other_table
;
If the variable is just a string then you cannot subtract from strings. You will have to convert the strings into numbers to perform arithmetic. You probably have too many decimal places for SAS datetime informats/formats to replicate (and perhaps to be uniquely stored in a floating point value) so just convert the date part and then append back the rest to keep the same time of day. Since dates are stored as number of days you can just subtract the 2 days using normal subtraction.
put(input(query_date,yymmdd10.)-2,yymmdd10.)||substr(query_date,11)
This should solve your problem. The first issue to tackle is converting the date string into a sas datetime. The input() function with the anydtdtm. informat accomplishes that with a small caveat as seen in the output.
data test;
date_txt = '2022-04-20-19.37.57.714699';
query_date = input(date_txt, anydtdtm.); * convert string into sas datetime;
calc_date = intnx('dtday', query_date, -2, 's'); * backup 2 days preserving the time;
format query_date calc_date e8601dt26.6;
run;
date_txt
query_date
calc_date
2022-04-20-19.37.57.714699
2022-04-20T19:37:57.000000
2022-04-18T19:37:57.000000
The default width of the informat is 19 characters which excludes the fractional seconds, but the informat correctly converted the string into a datetime.
To attempt to capture the full width of the date string, I modified the informat to anydtdtm26.. However, that change resulted in an error and missing values for query_date and calc_date. Although the anydtdtm informat is robust with converting a wide variety of date and time formats, I suspected that the problem lies with the periods used to delimit the hours and minutes.
To correct that problem I used prxchange() function to replace the periods after hours and minutes with colons which are standard time componenent delimiters. That change allows the anydtdtm informat to properly convert the fractional seconds.
data test2;
date_txt = '2022-04-20-19.37.57.714699';
date_mod = prxchange('s/\./:/', 2, date_txt); * replace first 2 periods w/ colons;
query_date = input(date_mod, anydtdtm26.); * convert string into sas datetime;
calc_date = intnx('dtday', query_date, -2, 's'); * backup 2 days preserving the time;
format query_date calc_date e8601dt26.6;
run;
date_txt
date_mod
query_date
calc_date
2022-04-20-19.37.57.714699
2022-04-20-19:37:57.714699
2022-04-20T19:37:57.714699
2022-04-18T19:37:57.714699
Although I used a data step to illustrate the solution the functions can also be used in a SQL statement.

Trailing zeros omitted when inserting to postgres

While inserting date with milliseconds part containing zero is skipping in PostgreSQL. If it have non-zero part in date, it will insert correctly into db.
eg: 2019-06-01 11:59:59:371Z this will insert correctly into db but when I am inserting '2010-06-21 11:59:59:010Z' then will become '2010-06-21 11:59:59:01Z' last zero part is skipped.
I used a query
SELECT to_char(date, 'YYYY-MM-DD HH:MI:SS:MSZ')
FROM table_name;
then I will get data correctly but it is in string format and if i changed to time.Time format then zero part is skipped.
Is any method to forcefully store milliseconds without skipping zero to postgres or any method to resolve this issue
2099-06-21T23:59:59.371Z -> 2099-06-21T23:59:59.371Z
2099-06-21T23:59:59.000Z -> 2099-06-21T23:59:59Z
2099-06-21T23:59:59.010Z -> 2099-06-21T23:59:59.01Z
2010-06-21T23:59:59.001Z -> 2010-06-21T23:59:59.001Z
2010-06-21T23:59:59.100Z -> 2010-06-21T23:59:59.1Z
Trailing zeros behind the decimal separator are irrelevant for the accuracy of a number.
If your application expects a certain fixed number of digits after the decimal separator, format the timestamp appropriately with the to_char function when you query the table.
If you want to avoid sprinkling your queries with to_char, define a view on the table that formats the timestamp as a string and use that view in your queries.
By all means, don't store timestamps as strings in a table. You would lose validity checks and date arithmetic, and you'd waste storage space.

Dividing large numbers in postgresql

I am working with numbers of 18 decimals, I have decided to save the number as a "NUMERIC (36)" in database
Now I want to present it by doing the following division
select (5032345678912345678::decimal / power(10, 18)::decimal )::decimal(36,18)
result
5.032345678912345700
expected result
5.032345678912345678
It works if I use a precision of 16 decimals
select (50323456789123456::decimal / power(10, 16)::decimal )::decimal(36,16)
result 5.0323456789123456
Any idea how to work with 18 decimals without losing information?
Use a constant typed as decimal(38,18):
select 5032345678912345678::decimal / 1000000000000000000::decimal(38,18);
?column?
----------------------
5.032345678912345678
(1 row)
A constant should be a bit faster. However the same cast should work for power(10,18) as well.

Convert String to Datetime or Datetime 2

I need help because I can't convert it by myself. I got the following String:
20160803093000000
Inside the String there is the Date and Time: 2016-08-03 09:30:00:00
I need it as Datetime or Datetime2.
Here it is with the milliseconds (I entered another date, so you can see the change in the milliseconds):
DECLARE #t VARCHAR(50)
SET #t = '20160803093012345'
SELECT CONVERT(DATETIME,STUFF(STUFF(STUFF(STUFF(#t,9,0,' '),12,0,':'),15,0,':'),18,0,'.'))
I read something about loss of precision at the millisecond level, though. So there is still some need for research on the topic.
If you need the precision of the milliseconds, you can use DATETIME2 and just do:
SELECT CONVERT(DATETIME2,STUFF(STUFF(STUFF(STUFF(#t,9,0,' '),12,0,':'),15,0,':'),18,0,'.'))
I just read a great article regarding the precision. It says:
If you want to store the exact same value you had in DATETIME, just
choose DATETIME2(3), you get the same precision but it only takes 7
bytes to store the value instead of 8.
Here is the link for more information.
SELECT CONVERT(DATETIME2,STUFF(STUFF(STUFF(STUFF(#t,9,0,' '),12,0,':'),15,0,':'),18,0,'.'))
Works fine! Thank you a lot.

Datastage.. I have a field which has the value say 59.0900..I want to convert it to 05909

I have a field which has the value say 59.0900..I want to convert it to 05909
Can somebody help me with that..Im working on datastage IBM infosphere
Convert the field into String
Trim the trailing zeros and decimal point
convert back to Decimal or integer