Oracle SQL - Same day last year (leap year proof) - sql

I'm looking to find the same day last year in oracle sql. For example Wednesday, March 16, 2016 would be Wednesday, March 18, 2015 for last year. So the closest day.
The following code worked good until the current leap year and broke after Feb 29th 2016.
this was my old statement which does not work for all dates:
NEXT_DAY(TRUNC(ADD_MONTHS(date, -12), 'iw')-2, TO_CHAR(date, 'DY'))

Simply subtract 7*52 = 364 days instead :-)

I think this may be correct
SQL Test:
WITH data
AS ( SELECT SYSDATE - (LEVEL - 1) this_year_date,
TO_NUMBER (TO_CHAR (SYSDATE - (LEVEL - 1), 'D'))
this_year_day_of_week,
ADD_MONTHS (SYSDATE - (LEVEL - 1), -12) last_year_date,
TO_NUMBER (
TO_CHAR (ADD_MONTHS (SYSDATE - (LEVEL - 1), -12), 'D'))
last_year_day_of_week
FROM DUAL
CONNECT BY LEVEL <= 300),
crunching
AS (SELECT data.*,
(CASE
WHEN this_year_day_of_week > last_year_day_of_week
THEN
this_year_day_of_week - last_year_day_of_week
WHEN this_year_day_of_week = last_year_day_of_week
THEN
0
ELSE
last_year_day_of_week - this_year_day_of_week
END)
math
FROM data)
SELECT TO_CHAR (crunching.this_year_date, 'yyyy-MM-dd Day') ty_date,
math,
(CASE
WHEN math = 0
THEN
TO_CHAR (last_year_date, 'yyyy-MM-dd Day')
WHEN math > 2
THEN
TO_CHAR ( (last_year_date - math) + 7, 'yyyy-MM-dd Day')
ELSE
TO_CHAR (last_year_date + math, 'yyyy-MM-dd Day')
END)
final_answer
FROM crunching
SQL Fiddle: http://sqlfiddle.com/#!4/9eecb7d/18211
Output:
TY_DATE MATH FINAL_ANSWER
2016-03-17 Thursday 2 2015-03-19 Thursday
2016-03-16 Wednesday 2 2015-03-18 Wednesday
2016-03-15 Tuesday 2 2015-03-17 Tuesday
2016-03-14 Monday 5 2015-03-16 Monday
2016-03-13 Sunday 5 2015-03-15 Sunday
2016-03-12 Saturday 2 2015-03-14 Saturday
2016-03-11 Friday 2 2015-03-13 Friday
2016-03-10 Thursday 2 2015-03-12 Thursday
2016-03-09 Wednesday 2 2015-03-11 Wednesday
2016-03-08 Tuesday 2 2015-03-10 Tuesday
2016-03-07 Monday 5 2015-03-09 Monday
2016-03-06 Sunday 5 2015-03-08 Sunday
2016-03-05 Saturday 2 2015-03-07 Saturday
2016-03-04 Friday 2 2015-03-06 Friday
2016-03-03 Thursday 2 2015-03-05 Thursday
2016-03-02 Wednesday 2 2015-03-04 Wednesday
2016-03-01 Tuesday 2 2015-03-03 Tuesday
2016-02-29 Monday 5 2015-03-02 Monday
2016-02-28 Sunday 6 2015-03-01 Sunday
2016-02-27 Saturday 1 2015-02-28 Saturday
2016-02-26 Friday 1 2015-02-27 Friday
2016-02-25 Thursday 1 2015-02-26 Thursday
2016-02-24 Wednesday 1 2015-02-25 Wednesday
2016-02-23 Tuesday 1 2015-02-24 Tuesday
2016-02-22 Monday 1 2015-02-23 Monday
2016-02-21 Sunday 6 2015-02-22 Sunday
2016-02-20 Saturday 1 2015-02-21 Saturday
2016-02-19 Friday 1 2015-02-20 Friday
2016-02-18 Thursday 1 2015-02-19 Thursday
2016-02-17 Wednesday 1 2015-02-18 Wednesday

How about add_months( [date], -12 ) ?

Related

POSTGRESQL Join on generated string property

I got a table named work_schema_items which contains a couple of columns and the important one for this question is varchar column weekday. The contains the following values:
end_time start_time weekday
12:00 06:00 Monday
14:30 12:30 Monday
15:00 14:45 Monday
18:00 15:00 Monday
21:00 18:30 Monday
23:00 21:15 Monday
23:59 23:15 Monday
12:00 06:00 Tuesday
14:30 12:30 Tuesday
15:00 14:45 Tuesday
18:00 15:00 Tuesday
21:00 18:30 Tuesday
23:00 21:15 Tuesday
23:59 23:15 Tuesday
12:00 06:00 Wednesday
14:30 12:30 Wednesday
15:00 14:45 Wednesday
18:00 15:00 Wednesday
21:00 18:30 Wednesday
23:00 21:15 Wednesday
23:59 23:15 Wednesday
12:00 06:00 Thursday
14:30 12:30 Thursday
15:00 14:45 Thursday
18:00 15:00 Thursday
21:00 18:30 Thursday
23:00 21:15 Thursday
23:59 23:15 Thursday
12:00 06:00 Friday
14:30 12:30 Friday
15:00 14:45 Friday
18:00 15:00 Friday
21:00 18:30 Friday
23:00 21:15 Friday
23:59 23:15 Friday
When I run the following sql query:
select
t.date,
t.weekday,
work_schema_items.weekday
from (
select
dd::date as date,
to_char(dd, 'Day')::varchar as weekday
from
generate_series('2019-12-08'::timestamp, '2019-12-16'::timestamp, '1 day'::interval) dd
) as t
left join work_schema_items on t.weekday = work_schema_items.weekday
order by date
I get this result:
2019-12-08 Sunday NULL
2019-12-09 Monday NULL
2019-12-10 Tuesday NULL
2019-12-11 Wednesday Wednesday
2019-12-11 Wednesday Wednesday
2019-12-11 Wednesday Wednesday
2019-12-11 Wednesday Wednesday
2019-12-11 Wednesday Wednesday
2019-12-11 Wednesday Wednesday
2019-12-11 Wednesday Wednesday
2019-12-12 Thursday NULL
2019-12-13 Friday NULL
2019-12-14 Saturday NULL
2019-12-15 Sunday NULL
2019-12-16 Monday NULL
What I'm expecting is to get the weekday value for every day just like I do with Wednesday. How come only Wednesday is properly joined? What am I doing wrong?
Thanks
I found the solution.
char is a fixed length datatype and Wednesday is the longest weekday string. It keeps its width even if I did convert it to varchar which resulted in that I tried to join "Monday " to "Monday".
A simple TRIM() solves the problem:
select
t.date,
t.weekday,
work_schema_items.weekday
from (
select
dd::date as date,
TRIM(to_char(dd, 'Day')::varchar) as weekday
from
generate_series('2019-12-08'::timestamp, '2019-12-16'::timestamp, '1 day'::interval) dd
) as t
left join work_schema_items on t.weekday = work_schema_items.weekday
order by date

How to find first Thursday of a month using Oracle SQL?

How to find First Thursday of this month using oracle?
select trunc(sysdate, 'MM')firstday from dual;
above is getting firstday of this month
Try with the below SQL
SELECT NEXT_DAY(TRUNC(SYSDATE, 'MONTH'),'THURSDAY') FROM DUAL;
SQL Fiddle
If you just get the next Thursday after the first day of the month, the after part means that if the 1st is itself a Thursday you will acually get a date a week too late. For instance, 2018-02-01 was a Thursday, and putting that into next_day() gives you 2018-02-08.
You need to get the next Thursday after the last day of the previous month:
next_day(trunc(sysdate, 'MM') - 1, 'THU')
Demo of both values; look at February, March and November in particular:
with t (dt) as (
select add_months(date '2018-01-15' , level - 1) from dual connect by level <= 12
)
select next_day(trunc(dt, 'MM'), 'THU') as wrong,
next_day(trunc(dt, 'MM') - 1, 'THU') as ok
from t;
WRONG OK
---------- ----------
2018-01-04 2018-01-04
2018-02-08 2018-02-01
2018-03-08 2018-03-01
2018-04-05 2018-04-05
2018-05-03 2018-05-03
2018-06-07 2018-06-07
2018-07-05 2018-07-05
2018-08-02 2018-08-02
2018-09-06 2018-09-06
2018-10-04 2018-10-04
2018-11-08 2018-11-01
2018-12-06 2018-12-06

Skip Holidays in Business day Table

I am using the following script to determine what the business days are for each particular month.
DECLARE #startdate DATETIME
SET #startdate ='20170401'
;
WITH bd AS(
SELECT
DATEADD(DAY,
CASE
(DATEPART(WEEKDAY, DATEADD(MONTH, DATEDIFF(MONTH, 0, #startdate), 0)) + ##DATEFIRST - 1) % 7
WHEN 6 THEN 2
WHEN 7 THEN 1
ELSE 0
END,
DATEADD(MONTH, DATEDIFF(MONTH, 0, #startdate), 0)
) AS bd, 1 AS n
UNION ALL
SELECT DATEADD(DAY,
CASE
(DATEPART(WEEKDAY, bd.bd) + ##DATEFIRST - 1) % 7
WHEN 5 THEN 3
WHEN 6 THEN 2
ELSE 1
END,
bd.bd
) AS db,
bd.n+1
FROM bd WHERE MONTH(bd.bd) = MONTH(#startdate)
)
SELECT * INTO #BD
FROM (
SELECT 'BD'+ CAST(n AS VARCHAR(5)) AS Expected_Date_Rule, bd AS Expected_Calendar_Date
from bd
) AS x
The result of this table works fine. Bd is the the calendar days for the particular month and n is the business day number. The script does its job of not counting the weekend as a business day.
bd n
----------------------- -----------
2017-04-03 00:00:00.000 1
2017-04-04 00:00:00.000 2
2017-04-05 00:00:00.000 3
2017-04-06 00:00:00.000 4
2017-04-07 00:00:00.000 5
2017-04-10 00:00:00.000 6
2017-04-11 00:00:00.000 7
2017-04-12 00:00:00.000 8
2017-04-13 00:00:00.000 9
2017-04-14 00:00:00.000 10
2017-04-17 00:00:00.000 11
2017-04-18 00:00:00.000 12
2017-04-19 00:00:00.000 13
2017-04-20 00:00:00.000 14
2017-04-21 00:00:00.000 15
2017-04-24 00:00:00.000 16
2017-04-25 00:00:00.000 17
2017-04-26 00:00:00.000 18
2017-04-27 00:00:00.000 19
2017-04-28 00:00:00.000 20
2017-05-01 00:00:00.000 21
But then I notice that a potential issue will occur in July where the output will count the 4th of July as BD2 when it should be counted as BD3. Some had created a holiday table that is updated with all the holidays (excuse the bad spelling).
Holiday table
1 2017-01-01 New Year Day
4 2017-01-02 New Year Day-Follow
1 2017-01-16 MArtin Luther King Day
4 2017-01-17 MArtin Luther King Day-Follow
1 2017-02-20 Preseiednt Day
4 2017-02-21 Preseiednt Day-Follow
1 2017-05-29 Memorial Day
4 2017-05-30 Memorial Day-Follow
1 2017-07-04 Independence Day
4 2017-07-05 Independence Day-Follow
1 2017-09-04 Labour Day
4 2017-09-05 Labour Day-Follow
1 2017-10-09 Columbus Day
4 2017-10-10 Columbus Day-Follow
1 2017-11-10 Vetrans Day
4 2017-11-11 Vetrans Day-Follow
1 2017-11-23 ThanksGiving
1 2017-11-24 Day After Thanks Giving
4 2017-11-24 ThanksGiving-Follow
4 2017-11-25 Day After Thanks Giving-Follow
1 2017-12-25 Christmas
4 2017-12-26 Christmas-Follow
I was thinking there may be some way I can update my script to check the holiday table and skip the holiday and dont count it as a business day. Any tips?

how to get data until the next 1 number

I have a date table where I have assinged business days. I need to get business from, for example, 1-20 before the next number 1. How can I do that? Here is a smaller verion of my table:
Date Day BusinessDays
2015-05-01 Friday 1
2015-05-02 Saturday 2
2015-05-03 Sunday 2
2015-05-04 Monday 2
2015-05-05 Tuesday 3
2015-05-06 Wednesday 4
2015-05-07 Thursday 5
2015-05-08 Friday 6
2015-05-09 Saturday 7
2015-05-10 Sunday 7
2015-05-11 Monday 7
2015-05-12 Tuesday 8
2015-05-13 Wednesday 9
2015-05-14 Thursday 10
2015-05-15 Friday 11
2015-05-16 Saturday 12
2015-05-17 Sunday 12
2015-05-18 Monday 12
2015-05-19 Tuesday 13
2015-05-20 Wednesday 14
2015-05-21 Thursday 15
2015-05-22 Friday 16
2015-05-23 Saturday 17
2015-05-24 Sunday 17
2015-05-25 Monday 17
2015-05-26 Tuesday 17
2015-05-27 Wednesday 18
2015-05-28 Thursday 19
2015-05-29 Friday 20
*2015-05-30 Saturday 1
*2015-05-31 Sunday 1
*2015-06-01 Monday 1
*2015-06-02 Tuesday 2
*2015-06-03 Wednesday 3
I need to get data from 1 to 20 business days and don't include the numbers that starts again from one (for example exclude rows that have * in front). This needs to be dynamic. Since DayName will change for every number so I can't include that in my where clause.
Let me assume that you have a date in mind, so you want everything from that date to the next "1".
select t.*
from datetable t
where t.date >= '2015-05-01' and
t.date < (select min(t2.date)
from datetable t2
where t2.date > '2015-05-01' and
t2.businessdays = 1
);

sql code to print all the mondays of the year 2012

2012
January
2/1/2012
9/1/2012
16/1/2012
23/1/2012
30/1/2012
February
6/2/2012
.
.
.
.
and so on till decemeber.
the dates mentioned are mondays..How do i get the output in this format?? please help
One option
SQL> ed
Wrote file afiedt.buf
1 select next_day(trunc(sysdate,'YYYY') + 7*(level-1), 'MON' )
2 from dual
3* connect by level <= 53
SQL> /
NEXT_DAY(
---------
02-JAN-12
09-JAN-12
16-JAN-12
23-JAN-12
30-JAN-12
06-FEB-12
13-FEB-12
20-FEB-12
27-FEB-12
05-MAR-12
12-MAR-12
<<snip>>
03-DEC-12
10-DEC-12
17-DEC-12
24-DEC-12
31-DEC-12
53 rows selected.
Query:
select distinct next_day(to_date('01-01-2012','mm-dd-yyyy')-1+level,'Monday') All_MON from dual
connect by level <= floor(to_date('12-31-2011','mm-dd-yyyy') - to_date('01-01-2011','mm-dd-yyyy'))
order by All_MON;
Output:
01/02/2012 MONDAY
01/09/2012 MONDAY
01/16/2012 MONDAY
01/23/2012 MONDAY
01/30/2012 MONDAY
02/06/2012 MONDAY
02/13/2012 MONDAY
02/20/2012 MONDAY
02/27/2012 MONDAY
03/05/2012 MONDAY
03/12/2012 MONDAY
03/19/2012 MONDAY
03/26/2012 MONDAY
04/02/2012 MONDAY
04/09/2012 MONDAY
04/16/2012 MONDAY
04/23/2012 MONDAY
04/30/2012 MONDAY
05/07/2012 MONDAY
05/14/2012 MONDAY
05/21/2012 MONDAY
05/28/2012 MONDAY
06/04/2012 MONDAY
06/11/2012 MONDAY
06/18/2012 MONDAY
06/25/2012 MONDAY
07/02/2012 MONDAY
07/09/2012 MONDAY
07/16/2012 MONDAY
07/23/2012 MONDAY
07/30/2012 MONDAY
08/06/2012 MONDAY
08/13/2012 MONDAY
08/20/2012 MONDAY
08/27/2012 MONDAY
09/03/2012 MONDAY
09/10/2012 MONDAY
09/17/2012 MONDAY
09/24/2012 MONDAY
10/01/2012 MONDAY
10/08/2012 MONDAY
10/15/2012 MONDAY
10/22/2012 MONDAY
10/29/2012 MONDAY
11/05/2012 MONDAY
11/12/2012 MONDAY
11/19/2012 MONDAY
11/26/2012 MONDAY
12/03/2012 MONDAY
12/10/2012 MONDAY
12/17/2012 MONDAY
12/24/2012 MONDAY
12/31/2012 MONDAY