Sql queries regarding dates - sql

select sysdate
from dual;
SYSDATE
-------
03-May-17
select sysdate -1
from dual;
SYSDATE
-------
02-May-17
==============
If the run the below sql query
select TO_DATE('01-DEC-15','DD-MON-YYYY') - 1
FROM DUAL;
I am getting the answer
30-Nov-15
=====
Can any body help me understand this behaviour?

The documentation has a section on datetime/interval arithmetic. A number added to or subtracted from a date is treated as a fraction of a day:
For example, SYSDATE + 1 is tomorrow. SYSDATE - 7 is one week ago. SYSDATE + (10/1440) is ten minutes from now
As #mathguy pointed out, all dates in Oracle have a time component, and sysdate has the current server time. You can see what's happening if you display the full value of the date/time. You can do that explicitly with to_char() and a suitable format, but for brevity here I'll alter my session NLS settings:
alter session set nls_date_format = 'SYYYY-MM-DD HH24:MI:SS';
select sysdate, sysdate - 1
from dual;
SYSDATE SYSDATE-1
-------------------- --------------------
2017-05-03 16:17:47 2017-05-02 16:17:47
Both show the same time, but on different days.
You can truncate a date to reduce it's precision; by default trunc(sysdate) zeros the time elements so it becomes midnight today rather than the current time.
Your third example is slightly more interesting because you are using mismatching values and format masks; with a 2-digit year and a 4-digit YYYY mask the year doesn't end up as you might have expected:
select to_date('01-DEC-15','DD-MON-YYYY'), to_date('01-DEC-15','DD-MON-YYYY') - 1
from dual;
TO_DATE('01-DEC-15', TO_DATE('01-DEC-15',
-------------------- --------------------
0015-12-01 00:00:00 0015-11-30 00:00:00
That probably isn't what you wanted. The RRRR mask is more forgiving, as described in the manual:
select to_date('01-DEC-15','DD-MON-RRRR'), to_date('01-DEC-15','DD-MON-RRRR') - 1
from dual;
TO_DATE('01-DEC-15', TO_DATE('01-DEC-15',
-------------------- --------------------
2015-12-01 00:00:00 2015-11-30 00:00:00
but it would be better to provide a full 4-digit year anyway:
select to_date('01-DEC-2015','DD-MON-YYYY'), to_date('01-DEC-2015','DD-MON-YYYY') - 1
from dual;
TO_DATE('01-DEC-2015 TO_DATE('01-DEC-2015
-------------------- --------------------
2015-12-01 00:00:00 2015-11-30 00:00:00
or even more simply, use a date literal:
select date '2015-12-01', date '2015-12-01' - 1
from dual;
DATE'2015-12-01' DATE'2015-12-01'-1
-------------------- --------------------
2015-12-01 00:00:00 2015-11-30 00:00:00
This also avoids issues with the date language; 'APR' won't be recognised in sessions where the language - specifically NLS_DATE_LANGAUGE - isn't English (unless it's coincidentally a recognised abbreviation ion another language, but some months will still fail). It's better to use month numbers. If you must use names or abbreviations, you can specify the language they are in with the optional third argument to to_date(). But literals are still easier if you aren't converting from a string variable.

Related

Convert strange 5 digit number to normal Date in Oracle SQL

I have numbers like 42946 in a database column and i want to format it as a normal Date like dd/mm/yyyy.
I was searching a lot but i dont find anything usefull.
I tried to do TO_DATE(<date>,'J') but this doesn't work beacuse i think 42946 is the number of days between today and January 1 of 1900, and this method works only for dates that are between today and January 1 of 4712 BC.
I hope you can help me with this.
i think 42946 is the number of days between today and January 1 of 1900
You mean, number of days since 1st of January 1900?
If so, you don't have to do much - just add that value to date you specified and you'll get the result. Its (result's) datatype is DATE so you can display it any way you want, using the to_char function (or any other option you prefer):
SQL> select date '1900-01-01' + 42946 as result from dual;
RESULT
----------
01.08.2017
SQL>
Example of formatting it:
SQL> select to_char(date '1900-01-01' + 42946, 'dd-mon-yyyy', 'nls_date_language = english') as result from dual;
RESULT
-----------
01-aug-2017
SQL>

SQL - Julien Date (CYYDDD) to date

Unfortunately, this is my first approach with SQL!
I am creating with the following code a query between an oracle DB and Excel (Power Query).
select "$Table"."Order" as "Order",
"$Table"."NR" as "Nr",
"$Table"."JDDATE" as "JDDATE"
from "POOLDB2"."3112" "$Table"
WHERE "Key" >118001
AND "CodeAA" = 1
This code works!
Now I want to format the Julian Date (CYYDDD) - for example 118001 for the 01.01.2019 - to a normal date format.
Does anyone know, how to implement this into the code above?
Maybe something like :
select "$Table"."Order" as "Order",
"$Table"."NR" as "Nr",
DATEADD(DAY, JDDATE % 1000 - 1, DATEADD(year, JDDATE/1000, 0))
"$Table"."JDDATE" as "JDDATE"
from "POOLDB2"."3112" "$Table"
WHERE "Key" >118001
AND "CodeAA" = 1
Best regards
There are many different formats for Julian Date... In your use case, this should do it :
with t as (select 118001 jd from dual)
select to_char( to_date(to_char(1901 + floor(jd / 1000)),'YYYY') + mod(jd,1000) - 1, 'dd.mm.yyyy' ) from t
Yields : 01.01.2019
For Oracle,
select to_char(sysdate,'J') from dual; --To Julian Date
select to_date(2456143,'J') from dual; --To Normal Date
must work.
Edit: Sorry I didn't see oracle tag.
Edit: For the requested behavior by OP
select to_date(to_char(1901 + floor(118001 / 1000)),'YYYY') from dual;
You can use the 118001 value you have, split into separate year and day sections, by adding to the nominal starting date 1900-01-01 (based on your comment that 118001 is actually 2018-01-01, not 2019-01-01):
select date '1900-01-01'
+ floor(118001 / 1000) * interval '1' year
+ (mod(118001, 1000) - 1) * interval '1' day
from dual;
DATE'1900-
----------
2018-01-01
or by startng the fixed date a day earlier you can remove the explicit -1:
select date '1899-12-31'
+ floor(118019 / 1000) * interval '1' year
+ mod(118019, 1000) * interval '1' day
from dual;
DATE'1899-
----------
2018-01-19
This avoids having to build up a longer string to convert to a date, though you could do that (modifying #GMB's approach) as:
select to_date(to_char(1900 + floor(118001 / 1000)) || '-01-01', 'YYYY-MM-DD')
+ (mod(118001, 1000) - 1)
from dual;
You need to specify the month, at least, in the to_date() call as Oracle defaults to the current month if that is not supplied. That behaviour is tucked away in the documentation:
If you specify a date value without a time component, then the default time is midnight. If you specify a date value without a date, then the default date is the first day of the current month.
The first part of that is fairly well known and makes sense ; the second part is a bit less obvious, and doesn't make it clear that it applies to partial dates too - so ifyou don't supply a year then the current year is used; if you don't supply a month then the current month is used; but if you don't supply a day then the 1st is used.
You can see what it's doing with some test conversions:
select to_date('2018-12-25', 'YYYY-MM-DD') as demo_a,
to_date('12:34:56', 'HH24:MI:SS') as demo_b,
to_date('2019', 'YYYY') as demo_c,
to_date('07-04', 'MM-DD') as demo_d,
to_date('2019-01', 'YYYY-MM') as demo_e
from dual;
DEMO_A DEMO_B DEMO_C DEMO_D DEMO_E
------------------- ------------------- ------------------- ------------------- -------------------
2018-12-25 00:00:00 2018-12-01 12:34:56 2019-12-01 00:00:00 2018-07-04 00:00:00 2019-01-01 00:00:00

Difference of date with SYSDATE - Value give something?

When subtracting today's date like below gives correct output.
select
(TRUNC(to_date('25/05/2016','dd/mm/yyyy'))-TRUNC(to_date('02/01/2016','dd/mm/yyyy')))
from dual;
output : 144 days
When taking todays date ad sysdate below should give 144. but shows some other value? Why?
select
(to_date(SYSDATE,'dd/mm/yyyy'))-(to_date('02/01/2016','dd/mm/yyyy'))
from dual;
output: -730343 (shows some value).
When you do:
to_date(SYSDATE,'dd/mm/yyyy')
you're implicitly converting SYSDATE, which is already a date, to a string - using your NLS_DATE_FORMAT. From the result that seems to be DD-MON-RR. So you're really doing:
to_date(to_char(SYSDATE,'DD-MON-RR'),'dd/mm/yyyy')
The inner part gives you the string '25-MAY-16'. When you convert that back to a date with the yyyy mask you have a two-digit year, 16, which is interpreted as year 0016 rather than 2016. You'd actually get what you expect if you used rrrr instead, but that's a happy side effect, and it'll still break in session with different NLS settings:
select to_date(SYSDATE,'dd/mm/yyyy') as bad_nls,
to_char(to_date(SYSDATE,'dd/mm/yyyy'), 'YYYY-MM-DD') as bad_string,
to_date(SYSDATE,'dd/mm/rrrr') as ok_nls,
to_char(to_date(SYSDATE,'dd/mm/rrrr'), 'YYYY-MM-DD') as ok_string
from dual;
BAD_NLS BAD_STRING OK_NLS OK_STRING
--------- ---------- --------- ----------
25-MAY-16 0016-05-25 25-MAY-16 2016-05-25
Notice that with your current NLS mask and the implicit conversion to a string you can't tell the difference between the first and third result; but it's obvious that it's wrong when shown with a four-digit year in the second and fourth results.
With your implicit conversion you're comparing 0016-05-25 with 2016-01-02, and it is giving you -730343 as that's how many days there are in 2000 years, adjusted for the 144 days you expected the gap to be.
As Praveen already said you don't need to use to_date() for SYSDATE, and if you're trying to set the time portion to midnight you can just truncate it.
select date '2016-05-25' - date '2016-01-02' as diff1,
date '2016-05-25' - date '0016-05-25' as diff2,
date '0016-05-25' - date '2016-01-02' as diff3,
trunc(sysdate) - date '2016-01-02' as diff4
from dual;
DIFF1 DIFF2 DIFF3 DIFF4
---------- ---------- ---------- ----------
144 730487 -730343 144
More generally, though, don't use two-digit years (alas it seems we've already forgotten the lessons of Y2K!), and don't rely on NLS settings.
You don't need to convert a date [sysdate] to date using to_date
Use trunc to strip the time portion of the sysdate.
Try;
select
TRUNC(SYSDATE) - (to_date('02/01/2016','dd/mm/yyyy'))
from dual;

SQL UTC date instead of SYSDATE

Working on the following query.
SELECT MIN(departure_date), ch_invoice.invoice_id
FROM ch_invoice
INNER JOIN ch_trip
ON ch_invoice.invoice_id = ch_trip.invoice_id
WHERE departure_date < SYSDATE
AND service_rendered = 0
AND paid = 1
Group By ch_invoice.invoice_id
Since the database that it is hitting may be in a location where the date hasn't changed yet, and we are using UTC as a standard for our dates, is it possible to replace SYSDATE with something like UTCDATE? I just need to know what day UTC is currently at.
As AntDC suggested, you can use the SYS_EXTRACT_UTC() function. That converts a 'datetime with time zone' from whatever time zone it is in to UTC.
SYSDATE doesn't have a time zone. If you passed that in then it would be implicitly converted to the system time zone, so it would work, but you can use SYSTIMESTAMP instead as that is already zone-aware.
You said in a comment that you want the date, but all Oracle dates have a time component; you can get the result as a date type with the time set to midnight (which makes sense for the comparison you're doing) with TRUNC():
select systimestamp,
sys_extract_utc(systimestamp) as utctime,
trunc(sys_extract_utc(systimestamp)) as utcdate
from dual;
SYSTIMESTAMP UTCTIME UTCDATE
-------------------------------- ------------------------- -------------------
2016-04-08 17:18:27.352 +01:00 2016-04-08 16:18:27.352 2016-04-08 00:00:00
That's running on a server in the UK, so the local time is BST.
Since that doesn't involve a day change, the effect can be demonstrated using the session time instead of the server time:
alter session set time_zone = '+12:00';
select current_timestamp,
sys_extract_utc(current_timestamp) as utctime,
trunc(sys_extract_utc(current_timestamp)) as utcdate
from dual;
CURRENT_TIMESTAMP UTCTIME UTCDATE
-------------------------------- ------------------------- -------------------
2016-04-09 04:23:17.910 +12:00 2016-04-08 16:23:17.910 2016-04-08 00:00:00
My session time is 2016-04-09, but it still finds the UTC date as 2016-04-08.
(To be clear, I am not suggesting you switch to using current_timestamp or current_date; I'm using those instead of systimestamp and sysdate purely as a demo).
SELECT SYS_EXTRACT_UTC(departure_date)UTC_SYS, SYSTIMESTAMP FROM DUAL;

TO_CHAR and TO_DATE giving different results.How to achieve the TO_CHAR functionality using TO_DATE?

SELECT TO_CHAR((select logical_date -1 from logical_date
where logical_date_type='B'),'DD/MM/YYYY HH24:MI:SS') FROM DUAL;
This Query returns 23/04/2016 00:00:00
o/p of select logical_date -1 from logical_date where logical_date_type='B' :4/23/2016
SELECT TO_DATE((select logical_date -1 from logical_date
where logical_date_type='B'),'DD/MM/YYYY HH24:MI:SS') FROM DUAL;
This Query returns 4/23/0016.
How do I get the format given by TO_CHAR using TO_DATE ??
Dates do not have any intrinsic format. Oracle has an internal representation which doesn't look like anything you'd recognise as a date. The client or application decides how to display the date (e.g. using NLS_DATE_FORMAT), or you can use TO_CHAR to convert the date to a string with a format you specify, as you're doing in your first example.
In your second example you are doing an implicit comversion of your actual date to a string, using your NLS setting - losing the century in the process, so presumably that has YY rather than YYYY - and then you convert that back to a date. I'm surprised that doesn't error as you are swapping the month and day positions.
Do not do that. Converting to a string and back to a date is pointless, even if your settings don't lose information on the way.
If you want it as a date for a client or other process to use just do:
select logical_date -1
from logical_date
where logical_date_type='B'
If you want to compare against another date then still leave it as a date:
select other_columns
from logical_date
where logical_date_type='B'
and logical_date -1 < sysdate
If you want to specify the format for display then use:
select to_char(logical_date -1, 'DD/MM/YYYY')
from logical_date
where logical_date_type='B'
The problem is related to the default date format for your session that is configured in your oracle client settings
To check the NLS_DATE_FORMAT settings for your Session
SELECT value
FROM nls_session_parameters
WHERE parameter = 'NLS_DATE_FORMAT'
here's how you can change this setting for your session to achieve the desired results:
alter session set nls_date_format='DD/MM/YYYY HH24:MI:SS';
How do I get the format given by TO_CHAR using TO_DATE ?
Firstly, DATE doesn't have any format. Oracle does not store dates in the format you see. It stores it internally in 7 bytes with each byte storing different components of the datetime value.
Byte Description
---- -------------------------------------------------
1 Century value but before storing it add 100 to it
2 Year and 100 is added to it before storing
3 Month
4 Day of the month
5 Hours but add 1 before storing it
6 Minutes but add 1 before storing it
7 Seconds but add 1 before storing it
The format is only for display purpose. TO_DATE is used to convert a literal into date, and has nothing to do with formatting. To display a date in your desired format, use TO_CHAR with proper FORMAT MODEL.
Also, remember, formatting has an order of precedence:
Let's see the chronological order of precedence, i.e. starting from highest to least:
Using TO_CHAR or TO_DATE at the individual SQL statement
ALTER SESSION SET NLS_DATE_FORMAT=’whatever format model you want’;
Setting it as an OS environment variable on the client machine
Setting of NLS_DATE_FORMAT is in the database initialization parameters
For example,
Individual SQL statement:
SQL> SELECT HIREDATE, TO_CHAR(hiredate, 'YYYY-MM-DD') FROM emp;
HIREDATE TO_CHAR(HI
------------------- ----------
17/12/1980 00:00:00 1980-12-17
20/02/1981 00:00:00 1981-02-20
22/02/1981 00:00:00 1981-02-22
02/04/1981 00:00:00 1981-04-02
28/09/1981 00:00:00 1981-09-28
01/05/1981 00:00:00 1981-05-01
09/06/1981 00:00:00 1981-06-09
09/12/1982 00:00:00 1982-12-09
17/11/1981 00:00:00 1981-11-17
08/09/1981 00:00:00 1981-09-08
12/01/1983 00:00:00 1983-01-12
03/12/1981 00:00:00 1981-12-03
03/12/1981 00:00:00 1981-12-03
23/01/1982 00:00:00 1982-01-23
14 rows selected.
Session level:
SQL> alter session set nls_date_format='YYYY-MM-DD';
Session altered.
SQL> SELECT hiredate FROM emp;
HIREDATE
----------
1980-12-17
1981-02-20
1981-02-22
1981-04-02
1981-09-28
1981-05-01
1981-06-09
1982-12-09
1981-11-17
1981-09-08
1983-01-12
1981-12-03
1981-12-03
1982-01-23
14 rows selected.
SQL>
TO_DATE((select logical_date
This is wrong.
Never apply TO_DATE on a DATE column. It forces Oracle to:
first convert it into a string
then convert it back to date
based on the locale-specific NLS settings. You need TO_DATE to convert a literal into date. For date-arithmetic, leave the date as it is.