Months that ends with 29,30,31 ORACLE - sql

convert into date format oracle there is an identical question in the continuation of this discussion.
I have a field c_day in the table my_table that accepts numeric values ​​from 1 to 31. In this field. I need to add 210 days to today's date, and insert the value from c_day from the released date dd.mm.yyyy. For example, we take today's date 08.02.2023 and add 210 days to it, the date falls on September, and if c_day is 20, then the output should be 20.09.2023. But if c_dayis equal to 31, then of course the date should be set as 30.09.2023, because the last day of September is 30.
Now I settled on cases where September ends on the 30th, and the field takes values ​​from 1 to 31. How can I write a condition in such cases so that it takes the last day of the month?
I tried this one, but it doesn't work:
SELECT
C_DAY,
LEAST(
TO_DATE(TO_CHAR(TRUNC(SYSDATE) + 210, 'YYYY-MM-') || C_DAY, 'YYYY-MM-DD'),
CASE
WHEN C_DAY < TO_CHAR(LAST_DAY(TRUNC(SYSDATE) + 210), 'DD') THEN
last_day(TO_DATE(TO_CHAR(TRUNC(SYSDATE) + 210, 'YYYY-MM-') || C_DAY, 'YYYY-MM-DD'))
END
) as result
FROM MY_TABLE
ORDER BY 1

You can can do it without any string manipulation using:
SELECT C_DAY,
LEAST(
TRUNC(TRUNC(SYSDATE) + 210, 'MM') + C_DAY - 1,
LAST_DAY(TRUNC(SYSDATE) + 210)
) AS result
FROM MY_TABLE
ORDER BY c_day
Which, for the sample data:
CREATE TABLE my_table ( c_day ) AS
SELECT LEVEL FROM DUAL CONNECT BY LEVEL <= 31;
Outputs:
C_DAY
RESULT
1
2023-09-01 00:00:00
2
2023-09-02 00:00:00
3
2023-09-03 00:00:00
...
...
28
2023-09-28 00:00:00
29
2023-09-29 00:00:00
30
2023-09-30 00:00:00
31
2023-09-30 00:00:00
fiddle

Related

How to get first date and last date of all twelve months for given year in Oracle PLSQL?

If i give input year like '2021' i need result as below
Month Start Date End Date
1 1/1/2021 31/01/2021
2 1/2/2021 28/01/2021
.
.
.
.
.
.
.
.
.
12 1/12/2021 31/12/2021
Basically, it is about the row generator technique; there are plenty of them, pick any you want. (Have a look at OraFAQ).
For example:
SQL> with mon as
2 (select add_months(trunc(to_date(&par_year, 'yyyy'), 'yyyy'), level - 1) val
3 from dual
4 connect by level <= 12
5 )
6 select to_char(val, 'mm') mon,
7 val start_date,
8 last_day(val) end_date
9 from mon
10 order by 1;
Enter value for par_year: 2021
MO START_DATE END_DATE
-- ---------- ----------
01 01/01/2021 31/01/2021
02 01/02/2021 28/02/2021
03 01/03/2021 31/03/2021
04 01/04/2021 30/04/2021
05 01/05/2021 31/05/2021
06 01/06/2021 30/06/2021
07 01/07/2021 31/07/2021
08 01/08/2021 31/08/2021
09 01/09/2021 30/09/2021
10 01/10/2021 31/10/2021
11 01/11/2021 30/11/2021
12 01/12/2021 31/12/2021
12 rows selected.
SQL>
You could also use directly the model clause for that purpose.
SELECT n
, TO_DATE(&the_year||lpad(f, 2, '0'), 'YYYYMM') start_dt
, last_day(TO_DATE(&the_year||lpad(f, 2, '0'), 'YYYYMM')) end_dt
FROM DUAL
MODEL
DIMENSION by (1 as n)
MEASURES (1 as f)
RULES (
f[FOR n FROM 1 TO 12 INCREMENT 1 ] = cv(n)
)
;
The advantage of the model clause is if you later want to get the every other month, you just need to change the increment from 1 to 2. Or if you are looking for the quarter months of the year (January, April, Jully, October), you just need to change increment from 1 to 3, and so on...
Just replace 2021 in the query for your year
with months (m) as (
select 1 from dual union all
select m + 1 from months where m < 12
)
select
to_date('2021' || '-' || to_char(m) || '-01', 'YYYY-MM-DD') as first_day,
last_day(to_date('2021' || '-' || to_char(m) || '-01', 'YYYY-MM-DD')) as last_day
from months
You can try on this db<>fiddle
Try below query
WITH cte_date as(
SELECT
LEVEL Month_No,
to_date(to_char(LEVEL||'-2021'),'MM-YYYY') Start_Date FROM dual
CONNECT BY LEVEL <=12
)
SELECT Month_No, Start_Date, LAST_DAY(Start_Date) End_Date
FROM cte_date;
I'm a fan of recursive CTEs because they are part of Standard SQL. I would phrase this as:
with months (month, startdate) as (
select 1 as month, date '2021-01-01'
from dual
union all
select month + 1, add_months(startdate, 1)
from months
where month < 12
)
select month, startdate, last_day(startdate) as enddate
from months;
If you need an input year, there are different ways to accomplish it. But a simple way is to change the second line to:
select 1 as month, to_date(:year || '0101', 'YYYYMMDD')

Current day and month (dd/mm) by counting number of days since January 1

Please does anyone have an idea on how to calculate current date (only dd/mm) by counting the number of days since the 1st January?
You can just add 157 to the first day of the year:
select trunc(sysdate, 'YYYY') + 157 from dual;
TRUNC(SYSDATE,'YYYY')+157
-------------------------
2016-06-06
The trunc(sysdate, 'YYYY') part gives you the first day of the year. You can also use a fixed date:
select date '2016-01-01' + 157 from dual;
DATE'2016-01-01'+157
--------------------
2016-06-06
And you can use an interval rather than a plain number of days if you prefer:
select date '2016-01-01' + numtodsinterval(157, 'DAY') from dual;
DATE'2016-01-01'+NUMTODSINTERVAL(157,'DAY')
-------------------------------------------
2016-06-06
Read more about datetime/interval arithmetic.
Either way of you only want DD/MM then use to_char():
select to_char(date '2016-01-01' + 157, 'DD/mM') from dual;
TO_CH
-----
06/06

Insert weekly dates starting fridays

I'm trying to insert weekly dates in my table, the start date is always on Fridays and the end date is always on thursday. I'm using this code :
CREATE TABLE WEEK AS
WITH generator AS (
SELECT DATE '2015-01-02' + LEVEL - 1 dt
FROM dual
CONNECT BY LEVEL <= DATE '2016-01-21' - DATE '2015-01-02' + 1
)
SELECT to_char(dt, 'YYYY "SEM"IW') "KEY",
dt "DATE_START",
least(next_day(dt - 1, to_char(DATE '2015-01-08', 'DAY')),
last_day(dt)) "DATE_END"
FROM generator
WHERE to_char(dt, 'D') = to_char(DATE '2015-01-02', 'D');
The code is working for weeks on the same month, but if I have a starting date on a month and the finish date on the next month, there's no data inserting in my table.
For example :
Date_ START | DATE_END
29-05-2015 | 31-05-2015
05-06-2015 | 11-05-2015
Instead of 31-05-2015 I should have 04-06-2015.
I think the following is what you're after:
with generator as (select date '2015-05-29' + (level - 1)*7 dt
from dual
connect by level <= (date '2016-01-21' - date '2015-05-29')/7 + 1)
select to_char(dt, 'YYYY "SEM"IW') "KEY",
dt "DATE_START",
dt + 6 "DATE_END"
from generator;
KEY DATE_START DATE_END
---------- ---------- ----------
2015 SEM22 2015-05-29 2015-06-04
2015 SEM23 2015-06-05 2015-06-11
2015 SEM24 2015-06-12 2015-06-18
2015 SEM25 2015-06-19 2015-06-25
<snip>
2016 SEM01 2016-01-08 2016-01-14
2016 SEM02 2016-01-15 2016-01-21
This is assuming that the dates you have specified in the generator subquery have already been determined to be a Friday. Otherwise you could use something like trunc(<date> - 4, 'iw') + 4 or trunc(<date> + 3, 'iw') + 4 (depending on whether you want the previous or next Friday to be included for the date specified) to make sure that the seed date is definitely a Friday.
Maybe just an alternative you can try analytical function here. But as
suggested by Boniest "just adding days" will be better approach. Have
fun
WITH generator AS
(SELECT DATE '2015-01-02' + LEVEL - 1 dt
FROM dual
CONNECT BY LEVEL <= DATE '2016-01-21' - DATE '2015-01-02' + 1
)
-- select * from generator;
SELECT TO_CHAR(dt, 'YYYY "SEM"IW') "KEY",
dt "DATE_START",
lead(dt) over (ORDER BY (dt)) -1 "End Date"
FROM generator
WHERE TO_CHAR(dt, 'D') = TO_CHAR(DATE '2015-01-02', 'D');
----------------------------------OUTPUT-----------------------------------------
**KEY DATE_START End Date**
2015 SEM18 05/01/2015 05/07/2015
2015 SEM19 05/08/2015 05/14/2015
2015 SEM20 05/15/2015 05/21/2015
2015 SEM21 05/22/2015 05/28/2015
2015 SEM22 05/29/2015 06/04/2015
----------------------------------------------------------------------------------

Add 2 months to current timestamp

How can I add months to a timestamp value in Oracle? In my query, it's getting converted to date value instead:
SELECT add_months(current_timestamp,2)
FROM dual;
The actual output is:
ADD_MONTH
11-MAR-13
The expected output is:
2013-01-01 00:00:00.000000000+00:00
This will give you the date and the time as a TIMESTAMP data type:
select TO_TIMESTAMP(TO_CHAR(ADD_MONTHS(SYSDATE, 2), 'YYYYMMDD HH24:MI'),
'YYYYMMDD HH24:MI') from dual;
If you need more or less precision (E.G. rounding) than what is above, adjust the date formats (both need to be the same format). For example, this will return 2 months down to the seconds level of precision:
select TO_TIMESTAMP(TO_CHAR(ADD_MONTHS(SYSTIMESTAMP, 2),
'YYYYMMDD HH24:MI:SS'), 'YYYYMMDD HH24:MI:SS') from dual;
This is the closest I can get (as a character) to the format you need:
select TO_CHAR(
TO_TIMESTAMP(TO_CHAR(ADD_MONTHS(SYSTIMESTAMP, 2),
'YYYYMMDD HH24:MI:SS'), 'YYYY-MM-DD HH24:MI:SS'),
'YYYY-MM-DD HH24:MI:SS.FF TZR') from dual;
I think this will about give you what you're looking for:
SELECT TO_CHAR(TO_TIMESTAMP(ADD_MONTHS(CURRENT_TIMESTAMP,2))
+ (CURRENT_TIMESTAMP - TRUNC(CURRENT_TIMESTAMP)),
'YYYY-MM-DD HH:MI:SSxFFTZR') FROM DUAL;
The problem with using the interval methods is that you can get an unexpected error depending on the date you run the query. E.g.
SELECT TO_TIMESTAMP('31-JAN-2012') + NUMTOYMINTERVAL(1,'MONTH') FROM DUAL;
That query returns:
ORA-01839: date not valid for month specified
This is because it attempts to return February 31, which is not a valid date.
ADD_MONTHS is a "safer" way to date math, in that where the interval query would throw an error, ADD_MONTHS will return the last date of the month (Feb 28 or 29 depending on the year) in the above example.
For Oracle:
SELECT
TIMESTAMP'2014-01-30 08:16:32', -- TS we want to increase by 1 month
--TIMESTAMP'2014-01-30 08:16:32' + NUMTOYMINTERVAL(1, 'MONTH'), -- raises ORA-01839: date not valid for month specified
--TIMESTAMP'2014-01-30 08:16:32' + INTERVAL '1' MONTH, -- raises ORA-01839: date not valid for month specified
ADD_MONTHS(TIMESTAMP'2014-01-30 08:16:32', 1), -- works but is a date :(
CAST(ADD_MONTHS(TIMESTAMP'2014-01-30 08:16:32', 1) AS TIMESTAMP) -- works
FROM DUAL
SELECT current_timestamp + INTERVAL '2' MONTH from dual;
To display this in your desired format, use TO_CHAR:
SELECT TO_CHAR(current_timestamp + INTERVAL '2' MONTH,
'YYYY-MM-DD HH24:MI:SS.FF9TZH:TZM') from dual;
2013-03-11 23:58:14.789501000+01:00
For Oracle:
select TO_TIMESTAMP(Sysdate,'DD-Mon-YYYY HH24-MI-SS') + 60
from dual;
select sysdate + interval '2' month from dual;
select TO_TIMESTAMP (Sysdate + interval '2' month, 'DD-Mon-YYYY HH24-MI-SS')
from dual
;
Result1:
| TO_TIMESTAMP(SYSDATE,'DD-MON-YYYYHH24-MI-SS')+60 |
----------------------------------------------------
| March, 12 0013 00:00:00+0000 |
Result2:
| SYSDATE+INTERVAL'2'MONTH |
--------------------------------
| March, 11 2013 21:41:10+0000 |
Result3:
| TO_TIMESTAMP(SYSDATE+INTERVAL'2'MONTH,'DD-MON-YYYYHH24-MI-SS') |
------------------------------------------------------------------
| March, 11 0013 00:00:00+0000 |
sqlffidle demo.

Number of days in a month

I have a monthly amount that I need to spread equally over the number of days in the month. The data looks like this:
Month Value
----------- ---------------
01-Jan-2012 100000
01-Feb-2012 121002
01-Mar-2012 123123
01-Apr-2012 118239
I have to spread the Jan amount over 31 days, the Feb amount over 29 days and the March amount over 31 days.
How can I use PL/SQL to find out how many days there are in the month given in the month column?
SELECT CAST(to_char(LAST_DAY(date_column),'dd') AS INT)
FROM table1
Don't use to_char() and stuff when doing arithmetics with dates.
Strings are strings and dates are dates. Please respect the data types and use this instead:
1+trunc(last_day(date_column))-trunc(date_column,'MM')
Indeed, this is correct. It computes the difference between the value of the last day of the month and the value of the first (which is obviously always 1 and therefore we need to add this 1 again).
You must not forget to use the trunc() function if your date columns contains time, because last_day() preserves the time component.
SELECT EXTRACT(DAY FROM LAST_DAY(SYSDATE)) num_of_days FROM dual;
/
SELECT SYSDATE, TO_CHAR(LAST_DAY(SYSDATE), 'DD') num_of_days FROM dual
/
-- Days left in a month --
SELECT SYSDATE, LAST_DAY(SYSDATE) "Last", LAST_DAY(SYSDATE) - SYSDATE "Days left"
FROM DUAL
/
You can add a month and subtract the two dates
SQL> ed
Wrote file afiedt.buf
1 with x as (
2 select date '2012-01-01' dt from dual union all
3 select date '2012-02-01' from dual union all
4 select date '2012-03-01' from dual union all
5 select date '2012-01-31' from dual
6 )
7 select dt, add_months(trunc(dt,'MM'),1) - trunc(dt,'MM')
8* from x
SQL> /
DT ADD_MONTHS(TRUNC(DT,'MM'),1)-TRUNC(DT,'MM')
--------- -------------------------------------------
01-JAN-12 31
01-FEB-12 29
01-MAR-12 31
31-JAN-12 31
select add_months(my_date, 1)-my_date from dual;
SELECT TO_CHAR(LAST_DAY(SYSDATE), 'fmday-mon-rr dd') as LastDayOfMonth
FROM dual;
Use the following Oracle query:
select to_number(to_char(last_day(sysdate),'dd')) TotalDays from dual
Date_Parameter='01-Oct-2017'
select to_number(to_char(last_day('Date_Parameter'),'dd')) TotalDays from dual