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

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.

Related

Using the To_Date function is not converting to the expected format

I'm trying to convert a char (output from a substr operation) into a dateTime format using TO_DATE with the following format - hh24:mi:ss.
The output of the substr looks fine but as soon as I run it through the TO_DATE function it converts every row for this column into 01-MAR-22.
To demonstrate I have the following:-
SUS_TIME2 shows what I get back from the SUBSTR and it looks fine at this point. SUS_TIME3 then shows what I get back after running it through the TO_DATE function, this where it converts it to 01-MAR-22
,SUBSTR((s.resolve_date-suspend_date),-16,9) sus_time2
,TO_DATE(SUBSTR((s.resolve_date-suspend_date),-16,9), 'hh24:mi:ss') sus_time3
Can anyone see what's going on here please? Thanks
Dates are stored in an internal representation and do not have any intrinsic format. What you are seeing is how your client is choosing to format the actual date value as a string for display, using your session's NLS_DATE_FORMAT setting (not always the case, but this is SQL Developer, so it is here.)
When you call to_date() with only the time components it defaults the date part to the first day of the current month, which is why you are seeing March 1st. That is a bit buried 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.
But it is setting the time properly on that date, which you can see by either explicitly converting the date back to a string:
to_char(<your date>, 'YYYY-MM-DD HH24:MI:SS')
or by changing your session:
alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS'
db<>fiddle
But you should not rely on NLS settings for anything except ad hoc queries; someone else running your code may have different settings. And only convert back to a string at the last moment when you have to display the value in a particular format - store it and pass it around as a native date.
Oracle doesn't have a time-only data type, so if you really only care about the time part then you can use a date (either defaulting to current month, or using an explicit fixed date) and ignore the date part; or potentially use an interval; or use the number of seconds the time represents (i.e. 0-86399). Which is suitable depends on what you'll use the value for.
It looks like you might be substringing the result of subtracting two timestamps; in which case (a) you already have an interval, and (b) you probably need to allow for that difference to span more than one day. You can also extract the individual time components directly from an interval value. So I'd question whether your approach is really appropriate.
You appear to be calculating s.resolve_date-suspend_date which gives you an INTERVAL DAY TO SECOND data type (implying that one or both of s.resolve_date or suspend_date is a TIMESTAMP data type) and then using SUBSTR to extract the time component of the data type and trying to convert that to a date and then display the time component.
Don't do that as using SUBSTR is fragile as it depends on the number of decimal places that the INTERVAL has which, in turn, will depend on the number of fractional seconds that the TIMESTAMP values have.
Just pick a date and add the interval to it and then format it as string to display it:
SELECT s.resolve_date,
suspend_date,
TO_CHAR(
DATE '1900-01-01' + (s.resolve_date-suspend_date),
'hh24:mi:ss'
) sus_time
FROM table_name s
Which, for the sample data:
CREATE TABLE table_name (resolve_date, suspend_date) AS
SELECT CAST(SYSTIMESTAMP AS TIMESTAMP(6)),
CAST(TRUNC(SYSTIMESTAMP) AS TIMESTAMP(6))
FROM DUAL;
Outputs:
RESOLVE_DATE
SUSPEND_DATE
SUS_TIME
2022-03-25 12:59:15.223445
2022-03-25 00:00:00.000000
12:59:15
From your comment:
all i'm trying to do is format the data so that when it's exported to Excel sorting works correctly.
That really is an XY-problem. You can solve it in Excel by either specifying the column format as a time when you import the data into Excel or right-click on the column header and "Format" the column picking the "time" data type with the correct format model.
You can also output the time as a fraction of a day with a numeric data type and then Excel can format it as the correct time using either:
SELECT s.resolve_date,
suspend_date,
MOD(CAST(s.resolve_date AS DATE)-CAST(suspend_date AS DATE), 1) AS sus_time
FROM table_name s;
or
SELECT s.resolve_date,
suspend_date,
TO_CHAR(DATE '1900-01-01' + (s.resolve_date-suspend_date), 'SSSSS')
/ 86400 AS sus_time
FROM table_name s
Which both output:
RESOLVE_DATE
SUSPEND_DATE
SUS_TIME
2022-03-25 13:16:40.204461
2022-03-25 00:00:00.000000
.5532407407407407407407407407407407407407
Which may not be human readable in that format but Excel will reformat it in a time column to 13:16:40.
db<>fiddle here

Oracle to_date function usage

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.

Next Occurance of day of next week (Specifically, the next occurance of Wednesday) based on the current date

In Oracle SQL Developer, how does one determine the calendar date of the next occurrence of Wednesday, based on the current date. I have searched and seen many examples for Ruby, PHP, MYSQL, but not for oracle sql. I read this article about DATETIME calculations, but these we have not gone over in class. We are working with date conversion, case, when, timestamps, and intervals but I'm not sure how to go about this problem.
Nex_day function will return the date of the first weekday, specified as the second parameter of the function, later than a date specified as the first parameter of the function. For example:
SQL> select next_day(sysdate, 'WEDNESDAY') next_day
2 from dual
3 ;
NEXT_DAY
-----------
05-DEC-2012
You can use DATE function called NEXT_DAY
Try this
SELECT NEXT_DAY(SYSDATE,'WEDNESDAY') "Next WED"
FROM DUAL ;

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