convert oracle varchar date read into variable to linux date - sql

I am reading dates 'VARCHAR(14)' from an oracle table with a sql query in BASH, into a variable. I want to compare that date (eg. 20140611120520) with the system date. If the variable date is 15 minutes or older than the system date I need to kick off another procedure.
This is my query:
Get_einlag_time=`sqlplus ips_osmr/ips_osmr << eof | awk ' $1 == "This" { print $2 } '
select 'This ' || DZ_EINLAG
from LAG_SPI
where C_STATUS like '%E%' ;
eof
`
$Get_einlag_time - may result in more than one date seperated by a space (eg. 20140411141231 20140605075650)
I then read the dates one by one fom this variable with a For GET_TIME in $Get_einlag_time loop, comparing them with the system date.
Now I have this date value in $GET_TIME but I cannot do a date comparison as I get 'invalid date' from this variable.
This was my latest try in convertion before I gave up to ask for expert advice :-)
echo "time read from Get_einlag_time is $GET_TIME"
EINLAG_TIME=$(date +%Y%m%d -d "$GET_TIME")
EINLAG_CHK=$(date -d "$EINLAG_TIME+15 Minutes" +%Y%m%d%H%M%S)
EINLAG_SEC=$(date -d "$EINLAG_TIME" +%s)
COMPARE_TIME=$(date +%Y%m%d%H%M%S)
COMP_SEC=$(date +%s)
The $EINLAG_SEC converted %s gives me an extremely huge and unlikely value.

Perhaps you'd do it like this:
SELECT CAST ('This ' AS TIMESTAMP) || DZ_EINLAG AS This
from LAG_SPI
where C_STATUS like '%E%' ;
Not sure if the syntax works but you get the idea.
If it doesn't work you might as well refer to here for getting unix timestamps properly:
Convert timestamp datatype into unix timestamp Oracle
Actual probable solution:
GET_TIME=${R:0:4}/${R:4:2}/${R:6:2}\ ${R:8:2}:${R:10:2}:${R:12:2} ## Where R is like 20140411141231

Related

Shell script to delete records from Oracle DB table

I have a situation where I need to run a shell script every 30 days to delete records from Oracle database table.
The table has a column 'updated_date'. I need to write a query to delete records where 'updated_date' less than current date minus 30 days.
In Java I can do it comfortably. How to calculate the dynamic date and pass it to SQL query in Unix shell script.
Can someone please help me.
You could use the delete statement suggested by Littlefoot to utilize the system date from within the database.
But, since you asked -
How to calculate the dynamic date and pass it to SQL query in Unix
shell script,
This is how you can do it.
First, use the date command to get the current date in 'yyyy-mm-dd' format.
export dt=$(date +%Y-%m-%d)
You may then use the date variable within your shell script in sqlplus.
sqlplus -s userid/passwd#db_server<<EOF
delete from yourtable
where updated_date < DATE '${dt}' - 30;
commit;
exit
EOF
That would be something like this:
delete from your_table
where updated_date < trunc(sysdate) - 30;
SYSDATE is a function that returns current date (and time - that's why I used TRUNC function which will remove time component). It also means that you don't have to calculate date and pass it to SQL query - Oracle know it itself.
Though, note that SYSDATE shows database server date, so - if you work on different time zones, you might need to consider that fact.
Also, "minus 30 days": is it always 30 days, or did you actually mean "minus 1 month"? If so, you'd change the condition to
where updated_date < add_months(trunc(sysdate), -1)
and it'll take care about number of days in a month (30, 31; including February as well as leap years).

DB2 Convert Number to Date

For some reason (I have no control over this) dates are stored as Integers in an iSeries AS400 DB2 system that I need to query. E.g. today will be stored as:
20,171,221
Being in the UK I need it to be like the below in Date format:
21/12/2017
This is from my query: (OAORDT = date field)
Select
Date(SUBSTR( CHAR( OAORDT ),7,2) ||'/' || SUBSTR(CHAR ( OAORDT ),5,2) || '/' || SUBSTR(CHAR (OAORDT ),1,4)) AS "Order Date"
from some.table
However, all I get is Nulls. If I remove the Date function, then it does work but its now a string, which I don't want:
Select
SUBSTR( CHAR( OAORDT ),7,2) ||'/' || SUBSTR(CHAR ( OAORDT ),5,2) || '/' || SUBSTR(CHAR (OAORDT ),1,4) AS "Order Date"
from some.table
How do I convert the OAORDT field to Date?
Just to update - I will be querying this from MS SQL Server using an OpenQuery
Thanks.
1) How do I convert the OAORDT field to Date?
Simplest is to use TIMESTAMP_FORMAT :
SELECT DATE(TIMESTAMP_FORMAT(CHAR(OAORDT),'YYYYMMDD'))
2) Being in the UK I need it to be [...] in Date format 21/12/2017 :
SELECT VARCHAR_FORMAT(DATE(TIMESTAMP_FORMAT(CHAR(OAORDT),'YYYYMMDD')),'DD/MM/YYYY')
Note, you didn't specify where you are doing this, but since you tagged as ibm-midrange, I am answering for embedded SQL. If you want JDBC, or ODBC, or interactive SQL, the concept is similar, just the means of achieving it is different.
Make sure SQL is using dates in the correct format, it defaults to *ISO. For you it should be *EUR. In RPG, you can do it this way:
exec sql set option *datfmt = *EUR;
Make sure that set option is the first SQL statement in your program, I generally put it immediately between D and C specs.
Note that this is not an optimal solution for a program. Best practice is to set the RPG and SQL date formats both to *ISO. I like to do that explicitly. RPG date format is set by
ctl-opt DatFmt(*ISO);
SQL date format is set by
exec sql set option *datfmt = *ISO;
Now all internal dates are processed in *ISO format, and have no year range limitation (year can be 0001 - 9999). And you can display or print in any format you please. Likewise, you can receive input in any format you please.
Edit Dates are a unique beast. Not every language, nor OS knows how to handle them. If you are looking for a Date value, the only format you need to specify is the format of the string you are converting to a Date. You don't need to (can't) specify the internal format of the Date field, and the external format of a Date field can be mostly anything you want, and different each time you use it. So when you use TIMESTAMP_FORMAT() as #Stavr00 mentioned:
DATE(TIMESTAMP_FORMAT(CHAR(OAORDT),'YYYYMMDD'))
The format provided is not the format of the Date field, but the format of the data being converted to a Timestamp. Then the Date() function converts the Timestamp value into a Date value. At this point format doesn't matter because regardless of which external format you have specified by *DATFMT, the timestamp is in the internal timestamp format, and the date value is in the internal date format. The next time the format matters is when you present the Date value to a user as a string or number. At that point the format can be set to *ISO, *EUR, *USA, *JIS, *YMD, *MDY, *DMY, or *JUL, and in some cases *LONGJUL and the *Cxxx formats are available.
Since none of variants suited my needs I've came out with my own.
It is as simple as:
select * from yourschema.yourtable where yourdate = int(CURRENT DATE - 1 days) - 19000000;
This days thing is leap year-aware and suits most needs fine.
Same way days can be turned to months or years.
No need for heavy artillery like VARCHAR_FORMAT/TIMESTAMP_FORMAT.
Below worked for me:
select date(substring(trim(DateCharCol), 1, 2)||'/'||substring(trim(DateCharCol), 3, 2)||'/'||'20'||substring(trim(DateCharCol), 5, 2)) from yourTable where TableCol =?;

PROC SQL: Delete rows by Date with format of e8601dt

Having some issues deleting rows from a data set. They need to be deleted by a date criteria, but the variable is in e8601dt. format. One thing I noticed about the variable is that its a number type variable, but left aligned (not sure if that has relevance or not), so I attempted to substring, and some additional attempts (below)...no success -
PROC SQL;
DELETE *
FROM DATASETS.BATCH_REPORT
WHERE datepart(BATCH_DATE) > '2015-10-01'
;
QUIT;
PROC SQL;
DELETE *
FROM DATASETS.BATCH_REPORT
WHERE BATCH_DATE > '11oct2015'd
;
QUIT;
Assuming there has to be an easy way to call out a value in this format...or will I need to convert this variable to a more compliable format, then do my processing?
OK...did some research. Apparently (and some one please correct me if I am wrong)...to use the e8601dt. format, a date value needs to be multiplied by 86400, then you can apply the format. So.....dividing by 86400 brought me back to the SAS data as an integer. This did the trick :
PROC SQL;
DELETE *
FROM SETS
WHERE ID >= 20372
;
QUIT;
You're close! Date conversions are a pain between systems. The representation of the values depends on the environment configuration.
Within proc SQL, I think you have to specify oracle functions (not the SAS datepart) Looks like you've figured out that Oracle's 'DATE' datatype stores both date&time within the same value. The DATE datatype stores the year (including the century), the month, the day, the hours, the minutes, and the seconds (after midnight). SAS has 2 different date types: date and datetime.
I'd suggest using the oracle to_date() function to compare against a character date, i.e.
WHERE BATCH_DATE > to_date('2015-10-01','yyyy-mm-dd')
If desired, you could use the oracle to_char(BATCH_DATE,'mm-dd-yyyy') to cast the date variable to a text value and then compare on the text value. But you loose some of the comparison power.
....edited due to new info about ...ew... db2 ..... :-)
I'm way NOT a DB2 guy, but maybe something like this?
First, set the date as in: (the date passed to DB2 needs the double quotes):
CALL SYMPUT('INT_DATE',"'"||PUT(sas_date,YYMMDDD10.)||"'");
Then use in the SQL as in:
PROC SQL ;
WHERE BATCH_DATE >= &INT_DATE
https://communities.sas.com/t5/SAS-Procedures/DB2-Date9-format-To-SAS-Serial-Date/td-p/32436

Problems extending `format-string` with HH24MISS when converting decimal column based timestamp to actual timestamp

tl;dr
Why am I not able to convert following string timestamp
select timestamp_format('2015-08-21 000000', 'YYYY-MM-DD HH24MISS') as timestamp
from sysibm.sysdummy1;
on an i7.1.0/OS machine?
Especially since I can convert
select timestamp_format('000000' , 'HH24MISS') as timestamp
from sysibm.sysdummy1;
to:
timestamp
-------------------------
2015-08-01 00:00:00.000000
Context
On an i7.1.0/OS machine, I have a table with timestamp data split up in several decimal columns, like
declare global temporary table tstamp
(
year dec(4,0),
month dec(2,0),
day dec(2,0),
time dec(6,0)
);
with data like
insert into session.tstamp
values (2015,8,21,92601),
(2015,8,21,132601);
on which I want to do some date filtering. Given the somewhat inflexible format, I figured that it is probably better if I convert this to a timestamp and use this to query the table. So i consulted the i/OS 7.1 Manual on timestamp_format
I started of with building the date part, ending up with
select
timestamp_format(YEAR || '-' || MONTH || '-' || DAY, 'YYYY-MM-DD') as timestamp
from session.tstamp;
which returns
TIMESTAMP
--------------------------
2015-08-21 00:00:00.000000
2015-08-21 00:00:00.000000
Perfect, let us add the time part and explicit lpad it to contain six characters:
select
timestamp_format(YEAR || '-' || MONTH || '-' || DAY || ' ' || lpad(TIME, 6, '0'), 'YYYY-MM-DD HH24MISS') as timestamp
from session.tstamp;
This results in the following error:
SQL State: 22007
Vendor Code: -20448
Message: [SQ20448] Expression not valid using format string specified for TIMESTAMP_FORMAT. Cause . . . . . : Argument 1 of the TIMESTAMP_FORMAT function can not be interpreted using the format string specified in argument 2 for one of the following reasons: -- The string expression is too short or too long. -- The string expression does not conform to the template specified in the format string. -- Too many digits were specified in the string expression for the corresponding format element in the format string. -- A value in the string expression is not valid for the corresponding format element in the format string. Recovery . . . : Specify a valid format string for the function. Try the request again.
According to the manual regarding the format-string, a separator between fields is optional:
[...]
Two format elements can optionally be separated by one or more of the following separator characters:
[...]
Question
So why are not my values accepted when using 'YYYY-MM-DD HH24MISS' as the format-string, given that I explicit has bound the time length to six characters?
Side note
It is possible to use HH24MISS on its own as format-string, so I'm not really able to wrap my head around this.
select timestamp_format(lpad(TIME, 6, '0'), 'HH24MISS') as timestamp from session.tstamp;
TIMESTAMP
--------------------------
2015-08-01 13:26:01.000000
2015-08-01 09:26:01.000000
The difficulties described, are due to defects with the TIMESTAMP_FORMAT [aka TO_DATE] scalar. The requests shown are tested to have functioned, as expected, with the DB2 for IBM i 7.3 [and as a comment to the OP suggests, also on v7r2]. I had asked a similar question, "Why the failures using my earlier examples?" in SQL convert text mm/dd/yy to date and timestamp, but I have not yet re-visited those examples on the newer release. And FWiW, there may be some updated code on IBM i 7.1 for that feature with the latest code; I do not have that level of maintenance, so I can not test if the [the last of the] enhancements that are coming for that release include the code fixes that apparently exist in newer releases.
Note that the TO_DATE feature is not a true built-in [instead, is a system-supplied User Defined Function (UDF)], so personally, I would recommend an alternative; namely, writing and using a scalar UDF specific to the task, and/or choose a more compatible and easy way to generate the TIMESTAMP from those columns as they are defined. Consider the following expression [that assumes all dates are beyond year 1000, else the expression must change to use DIGITS(YEAR) vs just YEAR]:
timestamp( YEAR concat digits( MONTH ) concat digits( DAY )
concat digits( TIME )
)
A variation of that, is to use arithmetic to achieve the same effect of a 14–character timestamp-string form 'yyyymmddhhmmss':
timestamp( concat( YEAR * 10000 + MONTH * 100 + DAY
, digits ( TIME ) ) )
The following scalar function could be created to avoid coding the expression in [VIEW] queries or other places. As coded, with nothing but an expression on a RETURN statement, should allow in-lining; I did not specify any other potentially performance-related clauses such as parallel or on-null-input:
create function y4m2d2t6TS
( year dec(4, 0)
, month dec(2, 0)
, day dec(2, 0)
, time dec(6, 0)
) returns timestamp
language sql deterministic
return
digits( YEAR ) concat digits( MONTH )
concat digits( DAY ) concat digits( TIME )
; -- this semicolon is a statement separator, not terminator of above CREATE
select
y4m2d2t6TS( year, month, day, time ) as timestamp
from session.tstamp
; -- likeness of report from above query:
TIMESTAMP
2015-08-21-09.26.01.000000
2015-08-21-13.26.01.000000
******** End of data ***
You can use this in DB2 :
values(VARCHAR_FORMAT(current_date,'YYYY-MM-DD HH24:MI:SS'))
Bye

In Oracle, convert number(5,10) to date

When ececute the following SQL syntax in Oracle, always not success, please help.
40284.3878935185 represents '2010-04-16 09:18:34', with microsecond.
an epoch date of 01 January 1900 (like Excel).
create table temp1 (date1 number2(5,10));
insert into temp1(date1) values('40284.3878935185');
select to_date(date1, 'yyyy-mm-dd hh24:mi:ssxff') from temp1
Error report: SQL Error: ORA-01861: literal does not match format
string
01861. 00000 - "literal does not match format string"
*Cause: Literals in the input must be the same length as literals in
the format string (with the exception of leading whitespace). If the
"FX" modifier has been toggled on, the literal must match exactly,
with no extra whitespace.
*Action: Correct the format string to match the literal.
Thanks to Mark Bannister
Now the SQL syntax is:
select to_char(to_date('1899-12-30','yyyy-mm-dd') +
date1,'yyyy-mm-dd hh24:mi:ss') from temp1
but can't fetch the date format like 'yyyy-mm-dd hh24:mi:ss.ff'. Continue look for help.
Using an epoch date of 30 December 1899, try:
select to_date('1899-12-30','yyyy-mm-dd') + date1
Simple date addition doesn't work with timestamps, at least if you need to preserve the fractional seconds. When you do to_timestamp('1899-12-30','yyyy-mm-dd')+ date1 (in a comment on Mark's answer) the TIMESTAMP is implicitly converted to a DATE before the addition, to the overall answer is a DATE, and so doesn't have any fractional seconds; then you use to_char(..., '... .FF') it complains with ORA-01821.
You need to convert the number of days held by your date1 column into an interval. Fortunately Oracle provides a function to do exactly that, NUMTODSINTERVAL:
select to_timestamp('1899-12-30','YYYY-MM-DD')
+ numtodsinterval(date1, 'DAY') from temp3;
16-APR-10 09.18.33.999998400
You can then display that in your desired format, e.g. (using a CTE to provide your date1 value):
with temp3 as ( select 40284.3878935185 as date1 from dual)
select to_char(to_timestamp('1899-12-30','YYYY-MM-DD')
+ numtodsinterval(date1, 'DAY'), 'YYYY-MM-DD HH24:MI:SSXFF') from temp3;
2010-04-16 09:18:33.999998400
Or to restrict to thousandths of a second:
with temp3 as ( select 40284.3878935185 as date1 from dual)
select to_char(to_timestamp('1899-12-30','YYYY-MM-DD')+
+ numtodsinterval(date1, 'DAY'), 'YYYY-MM-DD HH24:MI:SS.FF3') from temp3;
2010-04-16 09:18:33.999
An epoch of 1899-12-30 sounds odd though, and doesn't correspond to Excel as you stated. It seems more likely that your expected result is wrong and it should be 2010-04-18, so I'd check your assumptions. Andrew also makes some good points, and you should be storing your value in the table in a TIMESTAMP column. If you receive data like this though, you still need something along these lines to convert it for storage at some point.
Don't know the epoch date exactly, but try something like:
select to_date('19700101','YYYYMMDD')+ :secs_since_epoch/86400 from dual;
Or, cast to timestamp like:
select cast(to_date('19700101', 'YYYYMMDD') + :secs_since_epoch/86400 as timestamp with local time zone) from dual;
I hope this doesn't come across too harshly, but you've got to totally rethink your approach here.
You're not keeping data types straight at all. Each line of your example misuses a data type.
TEMP1.DATE1 is not a date or a varchar2, but a NUMBER
you insert not the number 40284.3878935185, but the STRING >> '40284.3878935185' <<
your SELECT TO_DATE(...) uses the NUMBER Temp1.Date1 value, but treats it as a VARCHAR2 using the format block
I'm about 95% certain that you think Oracle transfers this data using simple block data copies. "Since each Oracle date is stored as a number anyway, why not just insert that number into the table?" Well, because when you're defining a column as a NUMBER you're telling Oracle "this is not a date." Oracle therefore does not manage it as a date.
Each of these type conversions is calculated by Oracle based on your current session variables. If you were in France, where the '.' is a thousands separator rather than a radix, the INSERT would completely fail.
All of these conversions with strings are modified by the locale in which Oracle thinks your running. Check dictionary view V$NLS_PARAMETERS.
This gets worse with date/time values. Date/time values can go all over the map - mostly because of time zone. What time zone is your database server in? What time zone does it think you're running from? And if that doesn't spin your head quite enough, check out what happens if you change Oracle's default calendar from Gregorian to Thai Buddha.
I strongly suggest you get rid of the numbers ENTIRELY.
To create date or date time values, use strings with completely invariant and unambiguous formats. Then assign, compare and calculate date values exclusively, e.g.:
GOODFMT constant VARCHAR2 = 'YYYY-MM-DD HH24:MI:SS.FFF ZZZ'
Good_Time DATE = TO_DATE ('2012-02-17 08:07:55.000 EST', GOODFMT);