Oracle to_date function usage - sql

When use of to_date function like
select to_date(17,'HH24') from dual
it return the 2015/3/1 17:00:00 which starts from the first day of the month. I wonder why the to_date function behave like this?
I expect it starts from the current day,
so it will show like '2015/3/31 17:00:00'.
How to deal with it?

If you are trying to get 17:00 on today's date you can do this:
trunc(sysdate) + 17/24

From a similar question: Oracle Get only time from To_Date() in a Query?
One of the anwsers may explain the behavior:
The reason this doesn't work is because this not a complete date. Even
when you use a to_date('07/12/2011','MM/DD/YYYY'), Oracle stores the
date and time, but makes all the components of the time ZERO. So the
actual date stored is 07/12/2011 HH:MI:SS
So maybe it stores correctly the year and the month but not the day.

Related

Convert 17 digit with decimal point timestamp in SQL to date

Trying to convert 43439.961377314816 into date. Currently I am using this code:
SELECT
(timestamp '1970-01-01 00:00:00 GMT' +
numtodsinterval(WRITETIMESTAMP, 'SECOND')) at time zone 'CST',
WRITETIMESTAMP
FROM
t.table
but I am getting this result:
01-JAN-70 06.03.59.961377315 AM CST
Date should be:
12/05/2018
This produces the date that you want:
select date '1899-12-30' + 43439.961377314816
from dual;
It looks like you are using Excel dates or something similar.
You have two problems in your query. First, you used the wrong base time. As pointed out by #GordonLinoff, the base time for an Excel date is actually 1900-01-01, and Excel treats 1900 as a leap year. This is not an error in Excel, per se, but a conscious design decision which was made to copy the (buggy) behavior of Lotus 1-2-3, which did have this bug. So - in Lotus 1-2-3 it's a bug, but in Excel it's a feature. :-) Secondly, in Excel dates the integer portion represents the number of days since the base date, and the fractional portion represent fraction of a day. In your NUMTODSINTERVAL call, however, you specified the interval_unit argument as 'SECOND'; it should have been 'DAY'.
Putting these things together we get
WITH cte AS (SELECT 43439.961377314816 AS WRITETIMESTAMP FROM DUAL)
SELECT
(timestamp '1899-12-30 00:00:00 GMT' + numtodsinterval(WRITETIMESTAMP, 'DAY')) at time zone 'CST',
WRITETIMESTAMP
FROM
cte
dbfiddle here
Best of luck.
This looks like expected behavior to me. 43439 seconds/60/60 = 12 hours and you're getting about 12 hours from the starting timestamp.
SELECT numtodsinterval('43439.961377314816', 'SECOND') as i FROM dual;
I
----------------------
+00 12:03:59.961377315
Why would you think that would give you a date in 2018?
Here is a working formula to put in Excel that works for Chromium browsers.
Chrome/Edge: =((Cell/1000000-11644473600)*1000000)/86400000000+DATE(1970,1,1)

Epoch date in the form 'YYYY-MM-DD"T"HH24:MI:SS"Z"'

I am new to SQL and have run into a problem. I want to have the epoch date i.e. 1970-01-01T00:00:00Z in this mentioned format.
I cannot use it as a constant (i.e. '1970-01-01T00:00:00Z') for programming reasons. I need a statement that gives this as an output. I have used this:
select to_char(TRUNC(add_months(sysdate,-555),'MM'), 'YYYY-MM-DD"T"HH24:MI:SS"Z"') from dual;
But the only problem with this statement is it will not give me the date I want next month i.e. it is month specific it will only work for April 2016. But I need a the date to always remain 1970-01-01T00:00:00Z.
Thanks in advance for the help.
PS: I am using Oracle SQL Developer (if that matters).
You can just do:
select '1970-01-01T00:00:00Z' from dual;
Or if you want to have it processed for some reason, which seems like pointless overhead:
select to_char(date '1970-01-01', 'YYYY-MM-DD"T"HH24:MI:SS"Z"') from dual;
Either will give you the string you want. But it is a string, not a date. If you want it as a proper data type (which I don't think you do, but maybe this is for comparison) it needs to be a timestamp with time zone, which you can get with:
select timestamp '1970-01-01 00:00:00 UTC' from dual;
Well this might not be the most elegent way but it is a way that will work:
select regexp_substr('1970-01-01T00:00:00Z','^....................',1,1) from dual;

how to get previous month of last day without using date function in oracle10g

How to get previous month of last day without using date function in oracle?
SQL> ed
Wrote file afiedt.buf
1 SELECT LAST_DAY(ADD_MONTHS(sysdate,-1))
2* from dual
SQL> /
LAST_DAY(
---------
30-JUN-14
But I want without using date function
Create a calendar table.
Example here:
http://social.technet.microsoft.com/wiki/contents/articles/22776.t-sql-calendar-table.aspx
Also this question:
Calendar table for Data Warehouse
You'll have to adjust the date functions to work in oracle. But this will let you do date logic without the database specific functions after the one-time load.
You haven't specified which functions you can and can't use, or why, so I don't know if this is allowed by whatever constraints you're trying to do this under:
select trunc(sysdate, 'MM') - 1
from dual;
TRUNC(SYSDATE,'MM')-1
---------------------
30-JUN-14
This form of the trunc() function gives you your date "with the time portion of the day truncated to the unit specified by the format model", although that's slightly misleading as you are not restricted to time format elements. Here I've used format model 'MM', so it truncates to the first of the current month. One day before that is the last day of the previous month.
But trunc() is still a date function, depending on your definition; and maybe more importantly it's still Oracle-specific syntax, which you might be trying to avoid for some reason. but then using sysdate wouldn't be allowed either.

Issue with date formatting SQL

SELECT LISTING_EOD.LOCATION, LISTING_EOD.APPTTIME, LISTING_EOD.PERSON_ID,
LISTING_EOD.FORENAME, LISTING_EOD.SURNAME, LISTING_EODS.STATUS,
LISTING_EOD.DBDATE
FROM DBNAME.LISTING_EOD LISTING_EOD;
This query returns a list of data processed today, I need to modify to check yesterday's data. I have tried add the below line of code, but it doesn't return anything. Does anyone know how I can achieve this?
where LISTING_EOD.DBDATE = '18-OCT-2012';
If you always want yesterday's data, rather than hard-coding the date you can use:
WHERE LISTING_EOD.DBDATE >= TRUNC(SYSDATE) - 1
AND LISTING_EOD.DBDATE < TRUNC(SYSDATE)
TRUNC(SYSDATE) gives you midnight this morning, so if run today it would give a range between 18-Oct-2010 00:00:00 and 18-Oct-2012 23:59:59.
It's generally not a good idea to use implicit date format masks; your original code assumes your NLS_DATE_FORMAT is set to DD-MON-YYYY, but that might not be correct now (if you're seeing the time in the existing select then it probably isn't), and may well not be in the future. Always use an explicit date format mask, like TO_DATE('18-OCT-2012', 'DD-MON-YYY'), to avoid ambiguity and unexpected behaviour.
If the field is actually VARCHAR2 rather than a DATE - which is bad - then you'll need to convert the date range to a string to get a match:
WHERE LISTING_EOD.DBDATE >= TO_CHAR(TRUNC(SYSDATE) - INTERVAL '1' DAY, 'DD-MON-YYYY HH24:MI:SS')
AND LISTING_EOD.DBDATE <= TO_CHAR(TRUNC(SYSDATE) - INTERVAL '1' SECOND, 'DD-MON-YYYY HH24:MI:SS')
That will work for a single day, just, but you'd have problems looking for a date range. It's much better and safer to store data in a column of the appropriate type.
Dates in Oracle by default contain time as well. If you just specify '18-OCT-2012', it will only match 18-OCT-2012 00:00:00'. One way to get around this is to format your database date to what you are comparing it to, e.g. to_char(LISTING_EOD.DBDATE, 'DD-MON-YYYY') and compare this to '18-OCT-2012'. This comparison will disregard time completely.
If you had a date variable to compare with instead of a string, format this using the same date mask used for the database date. This also gets around any assumptions abut default date format on the database in question.
I realised the 'table' I was querying was a view, examined it inside sqldeveloper, and added '-1' to the sysdate. This query then returned the previous days results.
I successfully retrieved the correct data, thanks for all help received.

Oracle to_date function with quarter-format

I need to find some records created in a range of quarters. For example, I'm looking for all records created between the 4th quarter of 2008 and the 1st quarter of 2010. I have this in my WHERE-clause:
...and r.record_create_date between to_date('2008 4','YYYY Q')
and to_date('2010 1','YYYY Q')
but Oracle says: ORA-01820: format code cannot appear in date input format. The Q is a valid date format symbol, so I'm not sure what's happened. Is this even a valid way to find values in between calender quarters, or is there a better way?
Also interesting, and possibly related, if I execute this:
select to_date('2009','YYYY') from dual;
The value displayed in my IDE is 2009-08-01. I would have expected 2009-08-04, since today is 2010-08-04.
This:
select to_date('2009 1','YYYY Q') from dual;
of course, fails.
(Oracle 10g)
Oracle says: ORA-01820: format code cannot appear in date input format. The Q is a valid date format symbol, so I'm not sure what's happened.
See the second column of table 2.15 at http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/sql_elements004.htm#i34948. Not all format elements are allowed when converting to dates, timestamps, etc.
I recommend against using between for date range checks. People often will miss values within the ending day that the expect to be included. So I would translate:
and r.record_create_date between to_date('2008 4','YYYY Q')
and to_date('2010 1','YYYY Q')
To
and to_date('2008-10-01', 'YYYY-MM-DD') <= r.record_create_date
and record_create_date < to_date('2010-04-01', 'YYYY-MM-DD') -- < beginning of 2Q2010.
Someone asked the same question on OTN: http://forums.oracle.com/forums/thread.jspa?threadID=1081398&tstart=255
The crux of the issue is that you can not specify "Q" in the TO_DATE function.
Given that you're already specifying a portion of the date, why not provide the entire date? Mind too that to_date('2010 1','YYYY Q') would give you Jan 1st, 2010 when you really want March 31st, 2010... at a second to midnight.
Since the relationship between quarters to months is one-to-many, it doesn't make sense to do TO_DATE('2008 1', 'yyyy q'); what date should be returned? The first of the quarter, the end of the quarter, ...? (On the other hand, converting a date to a quarter - like TO_CHAR(SYSDATE, 'yyyy q') makes sense because a specific date only exists in one quarter.)
So, if you do want a query that looks for a date that falls between two quarters, you will have to "rolll your own" (explicitly stating the dates of the start/end of a quarter.)
As a side note, in case anyone is considering not using TO_DATE please do not use things like: WHERE date_value BETWEEN 'date string1' and 'date string2' without the TO_DATE function. It assumes a default date format and under certain situations can avoid potentially useful indexes altogether.
Below is one example where the same query can have a different result.
select sysdate from dual where sysdate between '1-Jan-10' and '31-Dec-10';
SYSDATE
---------
04-AUG-10
SQL> alter session set nls_date_format = 'YYYY-MM-DD';
Session altered.
SQL> select * from dual where sysdate between '1-Jan-10' and '31-Dec-10';
no rows selected
(Notice that in the second instance no error is returned. It just assumes Jan 10, 0001 and Dec. 10th, 0031.)
I think the best way is to just input the quarter start date and quarter end dates without even bothering with to_date. I think if you use
between '1-Jan-10' and '31-Dec-10'
for example, then you don't (in Oracle I believe) need to_date and it isn't much more difficult than typing in the quarter number
To calculate in Oracle the first day of a quarter and the last day of a quarter from the year and quarter:
I Use the fact
start_month= -2 + 3 * quarter
last_month = 3 * quarter
variable v_year number
variable v_quarter number
exec :v_year :=2017
exec :v_quarter:=4
select :v_year as year,
:v_quarter as quarter,
to_date(:v_year||to_char(-2+3*:v_quarter,'fm00'),'yyyymm') as quarter_start,
last_day(to_date(:v_year||to_char(3*:v_quarter,'fm00')||'01 23:59:59','yyyymmdd hh24:mi:ss')) as quarter_end
from dual a;
YEAR|QUARTER|QUARTER_START |QUARTER_END
2017| 4|2017-10-01 00:00:00|2017-12-31 23:59:59