Oracle order by year, month - sql

I have a query which I want to order by year and then month. I have tryed order by to_date( depdate, 'mm' ) and TO_CHAR(depdate, 'YYYY/MM'). Here is an sqlfiddle to the table i am querying and the query itself sqlfiddle

You want to sort by the date value, not by the character string representation. That means that you also want to group by the date value. trunc(<<date column>>, 'mm') truncates a date to midnight on the first of the month. So something like this
SELECT to_char(trunc(DEPDATE,'MM'), 'Mon-YYYY') AS MONTH,
SUM(AMOUNTROOM) AS ROOMTOTAL,
SUM(AMOUNTEXTRAS) AS EXTRATOTAL,
SUM(AMOUNTEXTRAS + AMOUNTROOM) AS OATOTAL
FROM checkins
WHERE checkinstatus = 'D' AND depdate > TO_DATE('2013-12-01', 'yyyy/mm/dd')
AND depdate <= TO_DATE('2014-04-10', 'yyyy/mm/dd')
GROUP BY trunc(depdate,'mm')
ORDER BY trunc(depdate,'mm');
should be what you're looking for. See the updated fiddle

Check out this query. If it is a date field, just plain order by would work for you. You need not use TO_CHAR to convert to string and then sort:
WITH TAB AS
(
SELECT SYSDATE DATEVAL FROM DUAL
UNION
SELECT SYSDATE + 100 DATEVAL FROM DUAL
UNION
SELECT SYSDATE -500 DATEVAL FROM DUAL
UNION
SELECT SYSDATE + 30 DATEVAL FROM DUAL
UNION
SELECT SYSDATE -30 DATEVAL FROM DUAL
) SELECT * FROM TAB
ORDER BY DATEVAL DESC

Related

Creating a query with a column of dates that I specify without the ability to create a temporary table

I have read only access in my database and I have a table that looks like this;
I want an output that looks like the following
Is this possible? I think I would need to have a table of dates to pass through but I'm not sure how to go about that. Any help would be appreciated.
Use a sub-query factoring clause (WITH) to generate the dates to join:
WITH dates ( dt ) AS (
SELECT DATE '2016-07-01' FROM DUAL UNION ALL
SELECT DATE '2017-07-01' FROM DUAL UNION ALL
SELECT DATE '2019-08-01' FROM DUAL
)
SELECT EXTRACT(MONTH FROM d.dt) AS Month,
EXTRACT(YEAR FROM d.dt) AS Year,
NVL2(t.SomeValue, 'Y', 'N') AS "Change?"
FROM dates d
LEFT OUTER JOIN table_name t
ON ( TRUNC(t.effective_date, 'MM') = d.dt )
My desired output would include every month and year from 1/2012 on.
Then use a recursive sub-query factoring clause:
WITH dates ( dt ) AS (
SELECT DATE '2012-01-01' FROM DUAL
UNION ALL
SELECT ADD_MONTHS( dt, 1 )
FROM dates
WHERE ADD_MONTHS( dt, 1 ) <= SYSDATE
)
SELECT EXTRACT(MONTH FROM d.dt) AS Month,
EXTRACT(YEAR FROM d.dt) AS Year,
NVL2(t.SomeValue, 'Y', 'N') AS "Change?"
FROM dates d
LEFT OUTER JOIN table_name t
ON ( TRUNC(t.effective_date, 'MM') = d.dt )
You can use recursion to break the records in to who years...
WITH
fragmented (
window_start,
window_close,
some_value,
interval_start,
interval_close
)
AS
(
SELECT
window_start,
window_close,
some_value,
interval_start,
CASE
WHEN add_months(interval_start, 12) < window_close
THEN add_months(interval_start, 12)
ELSE window_close
END
AS interval_close
FROM
(
SELECT
effective_date AS window_start,
LEAD(effective_date) OVER (ORDER BY effective_date) AS window_close,
some_value,
effective_date AS interval_start
FROM
example
)
lookahead
UNION ALL
SELECT
window_start,
window_close,
some_value,
add_months(interval_start, 12),
CASE
WHEN add_months(interval_start, 24) < window_close
THEN add_months(interval_start, 24)
ELSE window_close
END
FROM
fragmented
WHERE
interval_close < window_close
)
SELECT
*
FROM
fragmented
ORDER BY
window_start,
interval_start
;
Demo : https://dbfiddle.uk/?rdbms=oracle_18&fiddle=ca1fef00069c178c28e09d209db35395

oracle sql how to get apart year and apart month from varchar

I have varchar column name Calc_code, with for example this varchar '100201', first two characters are Year (2010), third and fourth are month (02) and the last two characters are not related with date so they are not important. How will I convert into date so that I can in select in where clause give something like this:
select *
from table_a
where Calc_code >= p_year_from || p_month_from
and Calc_code <= p_year_to || p_month_to
You need the first 4 chars and you can get them with the function substr():
select *
from table_a
where substr(Calc_code, 1, 4) >= p_year_from || p_month_from
and substr(Calc_code, 1, 4) <= p_year_to || p_month_to
I assume that p_year_from, p_month_from, p_year_to and p_month_to are strings (since you concatenate them), padded with a 0 at the left if necessary.
Need to take the first four digits into consideration and then we can convert it using TO_DATE as following:
select to_date(SUBSTR('100201',1,4),'YYMM') FROM DUAL;
Note that output will be the first date of the month as the date is not given in TO_DATE function
Cheers!!
Use TO_DATE with the first 4 character substring of your value:
SELECT TO_DATE( SUBSTR( calc_code, 1, 4 ), 'RRMM' )
FROM DUAL;
So your assuming your p_year_from, etc values are numbers then the code would be:
SELECT *
FROM table_a
WHERE TO_DATE( SUBSTR( calc_code, 1, 4 ), 'RRMM' )
BETWEEN TO_DATE( TO_CHAR( p_year_from, '00' ) || TO_CHAR( p_month_from, '00' ), 'RRMM' )
AND TO_DATE( TO_CHAR( p_year_to, '00' ) || TO_CHAR( p_month_to, '00' ), 'RRMM' )
with YR_FROM as (select to_char(to_date(SUBSTR('100201',1,4),'YYMM'), 'YYYY-MM') FROM DUAL),--2010 Feb
YR_TO as (select to_char(to_date(SUBSTR('100301',1,4),'YYMM'), 'YYYY-MM') FROM DUAL) --2010 March
Select * from table_a
where to_char(CREATE_DATE, 'YYYY-MM') between (select * from YR_FROM) and (select * from YR_TO);
--Will give zero results
with YR as (select to_char(to_date(SUBSTR('100201',1,4),'YYMM'), 'YYYY-MM') FROM DUAL)
Select * from table_a
where to_char(CREATE_DATE, 'YYYY-MM') >= (select * from YR)
and to_char(CREATE_DATE, 'YYYY-MM') <=(select * from YR);

Check if current date is between two dates Oracle SQL

I would like to select 1 if current date falls between 2 dates through Oracle SQL.
I wrote an SQL after reading through other questions.
https://stackoverflow.com/questions/2369222/oracle-date-between-query
https://stackoverflow.com/questions/2399753/select-from-table-by-knowing-only-date-without-time-oracle
But it returned only null. sysdate is the current date that is 01/05/2014 in date format DD/MM/YYYY.
The SQL I wrote is:
select 1 from dual
WHERE to_date(sysdate,'DD/MM/YYYY')
BETWEEN TO_DATE('28/02/2014', 'DD/MM/YYYY')
AND TO_DATE('20/06/2014', 'DD/MM/YYYY');
and
select 1 from dual
WHERE to_date(sysdate,'DD/MM/YYYY') >= TO_DATE('28/02/2014', 'DD/MM/YYYY')
AND to_date(sysdate,'DD/MM/YYYY') < TO_DATE('20/06/2014', 'DD/MM/YYYY');
You don't need to apply to_date() to sysdate. It is already there:
select 1
from dual
WHERE sysdate BETWEEN TO_DATE('28/02/2014', 'DD/MM/YYYY') AND TO_DATE('20/06/2014', 'DD/MM/YYYY');
If you are concerned about the time component on the date, then use trunc():
select 1
from dual
WHERE trunc(sysdate) BETWEEN TO_DATE('28/02/2014', 'DD/MM/YYYY') AND
TO_DATE('20/06/2014', 'DD/MM/YYYY');
SELECT to_char(emp_login_date,'DD-MON-YYYY HH24:MI:SS'),A.*
FROM emp_log A
WHERE emp_login_date BETWEEN to_date(to_char('21-MAY-2015 11:50:14'),'DD-MON-YYYY HH24:MI:SS')
AND
to_date(to_char('22-MAY-2015 17:56:52'),'DD-MON-YYYY HH24:MI:SS')
ORDER BY emp_login_date
TSQL: Dates- need to look for gaps in dates between Two Date
select
distinct
e1.enddate,
e3.startdate,
DATEDIFF(DAY,e1.enddate,e3.startdate)-1 as [Datediff]
from #temp e1
join #temp e3 on e1.enddate < e3.startdate
/* Finds the next start Time */
and e3.startdate = (select min(startdate) from #temp e5
where e5.startdate > e1.enddate)
and not exists (select * /* Eliminates e1 rows if it is overlapped */
from #temp e5
where e5.startdate < e1.enddate and e5.enddate > e1.enddate);
In oracle use below,
select * from table_name where date_column_name between to_date('1-OCT-22') and ('31-OCT-22')
PS: replace table name and date column name and format accordingly

Specific day- sql oracle

How can I find a specific day no matter the year or date in sql oracle.
as an example
i have the folowing dates in and table
id date
1 2013-02-03
2 2013-01-04
3 2012-06-13
I want to find all the ids with the day= 13
I've did this already but it doesn't work
select id
from table
where dayofmonth(date) = 13
this works in mysql but not in oracle.
Can any of you help me?
You can either use to_char() + to_num()
select to_number(to_char(sysdate, 'DD')) from dual
or use the extract() function:
select extract (day from sysdate) from dual
EDIT:
To get all rows from a table whose day is 13, just use EXTRACT in the WHERE clause:
create table my_data as
(select 1 pk, to_date('2013-01-13', 'YYYY-MM-DD') as my_date from dual union all
select 1 pk, to_date('2013-01-14', 'YYYY-MM-DD') as my_date from dual union all
select 1 pk, to_date('2013-02-13', 'YYYY-MM-DD') as my_date from dual);
select * from my_data
where extract(day from my_date) = 13;

ADD_MONTHS function does not return the correct date in Oracle

See the results of below queries:
>> SELECT ADD_MONTHS(TO_DATE('30-MAR-11','DD-MON-RR'),-4) FROM DUAL;
30-NOV-10
>> SELECT ADD_MONTHS(TO_DATE('30-NOV-10','DD-MON-RR'),4) FROM DUAL;
31-MAR-11
How can I get '30-MAR-11' when adding 4 months to some date?
Please help.
There is another question here about Oracle and Java
It states that
From the Oracle reference on add_months http://download-west.oracle.com/docs/cd/B19306_01/server.102/b14200/functions004.htm
If date is the last day of the month or if the resulting month has fewer days than the day component of date, then the result is the last day of the resulting month. Otherwise, the result has the same day component as date.
So I guess you have to manually check stating day and ending day to change the behaviour of the function. Or maybe by adding days instead of months. (But I didn't find a add_day function in the ref)
As a workaround, I might possibly use this algorithm:
Calculate the target date TargetDate1 using ADD_MONTHS.
Alternatively calculate the target date TargetDate2 like this:
1) apply ADD_MONTHS to the first of the source date's month;
2) add the difference of days between the source date and the beginning of the same month.
Select the LEAST between the TargetDate1 and TargetDate2.
So in the end, the target date will contain a different day component if the source date's day component is greater than the number of day in the target month. In this case the target date will be the last day of the corresponding month.
I'm not really sure about my knowledge of Oracle's SQL syntax, but basically the implementation might look like this:
SELECT
LEAST(
ADD_MONTHS(SourceDate, Months),
ADD_MONTHS(TRUNC(SourceDate, 'MONTH'), Months)
+ (SourceDate - TRUNC(SourceDate, 'MONTH'))
) AS TargetDate
FROM (
SELECT
TO_DATE('30-NOV-10', 'DD-MON-RR') AS SourceDate,
4 AS Months
FROM DUAL
)
Here is a detailed illustration of how the method works:
SourceDate = '30-NOV-10'
Months = 4
TargetDate1 = ADD_MONTHS('30-NOV-10', 4) = '31-MAR-11' /* unacceptable */
TargetDate2 = ADD_MONTHS('01-NOV-10', 4) + (30 - 1)
= '01-MAR-11' + 29 = '30-MAR-11' /* acceptable */
TargetDate = LEAST('31-MAR-11', '30-MAR-11') = '30-MAR-11'
And here are some more examples to show different cases:
SourceDate | Months | TargetDate1 | TargetDate2 | TargetDate
-----------+--------+-------------+-------------+-----------
29-NOV-10 | 4 | 29-MAR-11 | 29-MAR-11 | 29-MAR-11
30-MAR-11 | -4 | 30-NOV-10 | 30-NOV-10 | 30-NOV-10
31-MAR-11 | -4 | 30-NOV-10 | 01-DEC-10 | 30-NOV-10
30-NOV-10 | 3 | 28-FEB-11 | 02-MAR-11 | 28-FEB-11
You can use interval arithmetic to get the result you want
SQL> select date '2011-03-30' - interval '4' month
2 from dual;
DATE'2011
---------
30-NOV-10
SQL> ed
Wrote file afiedt.buf
1 select date '2010-11-30' + interval '4' month
2* from dual
SQL> /
DATE'2010
---------
30-MAR-11
Be aware, however, that there are pitfalls to interval arithmetic if you're working with days that don't exist in every month
SQL> ed
Wrote file afiedt.buf
1 select date '2011-03-31' + interval '1' month
2* from dual
SQL> /
select date '2011-03-31' + interval '1' month
*
ERROR at line 1:
ORA-01839: date not valid for month specified
How about something like this:
SELECT
LEAST(
ADD_MONTHS(TO_DATE('30-MAR-11','DD-MON-RR'),-4),
ADD_MONTHS(TO_DATE('30-MAR-11','DD-MON-RR')-1,-4)+1
)
FROM
DUAL
;
Result: 30-NOV-10
SELECT
LEAST(
ADD_MONTHS(TO_DATE('30-NOV-10','DD-MON-RR'),4),
ADD_MONTHS(TO_DATE('30-NOV-10','DD-MON-RR')-1,4)+1
)
FROM
DUAL
;
Result: 30-MAR-11
the add_months function returns a date plus n months.
Since 30th November is the last date of the month, adding 4 months will result in a date that's the end of 4 months. This is expected behavior. If the dates are not bound to change, a workaround is to subtract a day after the new date has been returned
SQL> SELECT ADD_MONTHS(TO_DATE('30-NOV-10','DD-MON-RR'),4) -1 from dual;
ADD_MONTH
---------
30-MAR-11
SELECT TO_DATE('30-NOV-10','DD-MON-RR') +
(
ADD_MONTHS(TRUNC(TO_DATE('30-NOV-10','DD-MON-RR'),'MM'),4) -
TRUNC(TO_DATE('30-NOV-10','DD-MON-RR'),'MM')
) RESULT
FROM DUAL;
This section in paranthesis:
ADD_MONTHS(TRUNC(TO_DATE('30-NOV-10','DD-MON-RR'),'MM'),4) - TRUNC(TO_DATE('30-NOV-10','DD-MON-RR'),'MM')
gives you number of days between the date you entered and 4 months later. So, adding this number of days to the date you given gives the exact date after 4 months.
Ref: http://www.dba-oracle.com/t_test_data_date_generation_sql.htm
Simple solution:
ADD_MONTHS(date - 1, x) + 1
Here is the trick:
select add_months(to_date('20160228', 'YYYYMMDD')-1, 1)+1 from dual;
Enjoy!
We have come to simpler (in our understanding) solution to this problem - take the least day number from original and add_month result dates, as this:
TRUNC(ADD_MONTHS(input_date,1),'MM') + LEAST(TO_CHAR(input_date, 'DD'), TO_CHAR(ADD_MONTHS(input_date,1), 'DD')) - 1
Some other examples here do not work on every date, below our test results:
WITH DATES as (
SELECT TO_DATE('2020-01-31', 'YYYY-MM-DD HH24:MI:SS') as input_date,
'2020-02-29' as expected_date
FROM dual
UNION ALL
SELECT TO_DATE('2020-02-28', 'YYYY-MM-DD HH24:MI:SS'),
'2020-03-28'
FROM dual
UNION ALL
SELECT TO_DATE('2020-09-30', 'YYYY-MM-DD HH24:MI:SS'),
'2020-10-30'
FROM dual
UNION ALL
SELECT TO_DATE('2020-09-01', 'YYYY-MM-DD HH24:MI:SS'),
'2020-10-01'
FROM dual
UNION ALL
SELECT TO_DATE('2019-01-30', 'YYYY-MM-DD HH24:MI:SS'),
'2019-02-28'
FROM dual
UNION ALL
SELECT TO_DATE('2020-02-29', 'YYYY-MM-DD HH24:MI:SS'),
'2020-03-29'
FROM dual
UNION ALL
SELECT TO_DATE('2020-09-29', 'YYYY-MM-DD HH24:MI:SS'),
'2020-10-29'
FROM dual
UNION ALL
SELECT TO_DATE('2020-03-01', 'YYYY-MM-DD HH24:MI:SS'),
'2020-04-01'
FROM dual
),
methods as (
SELECT
input_date,
expected_date,
ADD_MONTHS(input_date,1) as standard_way,
add_months(input_date-1, 1)+1 as wrong_way,
TO_DATE(LEAST(TO_CHAR(input_date, 'DD'), TO_CHAR(ADD_MONTHS(input_date,1), 'DD')) || '-' || TO_CHAR(ADD_MONTHS(input_date,1), 'MM-YYYY'), 'DD-MM-YYYY') as good_way,
TRUNC(ADD_MONTHS(input_date,1),'MM') + LEAST(TO_CHAR(input_date, 'DD'), TO_CHAR(ADD_MONTHS(input_date,1), 'DD')) - 1 as better_way
FROM
DATES
)
SELECT
input_date,
expected_date,
standard_way,
CASE WHEN TO_CHAR(standard_way,'YYYY-MM-DD') = expected_date THEN 'OK' ELSE 'NOK' END as standard_way_ok,
wrong_way,
CASE WHEN TO_CHAR(wrong_way,'YYYY-MM-DD') = expected_date THEN 'OK' ELSE 'NOK' END as wrong_way_ok,
good_way,
CASE WHEN TO_CHAR(good_way,'YYYY-MM-DD') = expected_date THEN 'OK' ELSE 'NOK' END as good_way_ok,
better_way,
CASE WHEN TO_CHAR(better_way,'YYYY-MM-DD') = expected_date THEN 'OK' ELSE 'NOK' END as better_way_ok
FROM
methods
;
CREATE OR REPLACE FUNCTION My_Add_Month(
STARTDATE DATE,
MONTHS_TO_ADD NUMBER
)
RETURN DATE
IS
MY_ADD_MONTH_RESULT DATE;
BEGIN
SELECT ORACLES_ADD_MONTH_RESULT + NET_DAYS_TO_ADJUST INTO MY_ADD_MONTH_RESULT FROM
(
SELECT T.*,CASE WHEN SUBSTRACT_DAYS > ADD_DAYS THEN ADD_DAYS - SUBSTRACT_DAYS ELSE 0 END AS NET_DAYS_TO_ADJUST FROM
(
SELECT T.*,EXTRACT(DAY FROM ORACLES_ADD_MONTH_RESULT) AS SUBSTRACT_DAYS FROM
(
SELECT ADD_MONTHS(STARTDATE,MONTHS_TO_ADD) AS ORACLES_ADD_MONTH_RESULT,EXTRACT(DAY FROM STARTDATE) AS ADD_DAYS FROM DUAL
)T
)T
)T;
RETURN TRUNC(MY_ADD_MONTH_RESULT);
END My_Add_Month;
/
--test & verification of logic & function both
SELECT T.*,ORACLES_ADD_MONTH_RESULT + NET_DAYS_TO_ADJUST AS MY_ADD_MONTH_RESULT,
My_Add_Month(STARTDATE,MONTHS_TO_ADD) MY_ADD_MONTH_FUNCTION_RESULT
FROM
(
SELECT T.*,CASE WHEN SUBSTRACT_DAYS > ADD_DAYS THEN ADD_DAYS - SUBSTRACT_DAYS ELSE 0 END AS NET_DAYS_TO_ADJUST FROM
(
SELECT T.*,EXTRACT(DAY FROM ORACLES_ADD_MONTH_RESULT) AS SUBSTRACT_DAYS FROM
(
SELECT T.*,ADD_MONTHS(STARTDATE,MONTHS_TO_ADD) AS ORACLES_ADD_MONTH_RESULT,EXTRACT(DAY FROM STARTDATE) AS ADD_DAYS FROM
(
SELECT TO_DATE('28/02/2014','DD/MM/YYYY') AS STARTDATE, 1 AS MONTHS_TO_ADD FROM DUAL
)T
)T
)T
)T;
Query-result
STARTDATE 2/28/2014
MONTHS_TO_ADD 1
ORACLES_ADD_MONTH_RESULT 3/31/2014
ADD_DAYS 28
SUBSTRACT_DAYS 31
NET_DAYS_TO_ADJUST -3
MY_ADD_MONTH_RESULT 3/28/2014
MY_ADD_MONTH_FUNCTION_RESULT 3/28/2014