How do I generate a list of dates of every 7 days between a range of dates based on a date field in Oracle SQL? - sql

I need to generate a list of week end dates based on the first week end date, and between a date range.
For example: I have a week end date of '06/04/2022'. If I enter 06/01/2022-01/01/2023 as my date parameters, I need a list of every seven days beginning on 06/04/2022 through 01/01/2023. The output would look something like this:
Dates
06/04/2022
06/11/2022
06/18/2022
.
.
.
12/24/2022
12/31/2022
Note: the initial week end date is not necessarily always on a Saturday, so it would need to be based on the actual date and not the day of the week.
I have this code which produces every day between two dates, but I need every seven days between two dates, and based on a date field retrieved from another table. I'm stuck on how to get every seven days, or every week from the date.
select (date'2022-06-04' + level - 1) dt
from dual
connect by level <= (date'2023-01-01' - date'2022-06-01' + 1)

Multiply the step size by 7:
SELECT DATE '2022-06-04' + (level - 1) * 7 AS dt
FROM dual
CONNECT BY DATE '2022-06-04' + (level - 1) * 7 <= DATE '2023-01-01'
Or, to make the calculation you are preforming more obvious that you are adding a week, you can use an INTERVAL literal:
SELECT DATE '2022-06-04' + (level - 1) * INTERVAL '7' DAY AS dt
FROM dual
CONNECT BY DATE '2022-06-04' + (level - 1) * INTERVAL '7' DAY <= DATE '2023-01-01'

You could also calculate the number of weeks required in your connect by levels code.
i.e. This would give you the number of rows required
CONNECT BY LEVEL < (DATE2 - DATE1) / 7
Notably the DATE2 - DATE1 code works because Oracle likes to think of things in terms of days. i.e. Sysdate - 1 is just yesterday. Sysdate + 1 is just tomorrow.
Then tack on a + 1 since you probably want to return 1 row in the case it's less than a week
And then doing this
SELECT DATE'2023-01-24' + (LEVEL-1) * 7
FROM DUAL
CONNECT BY LEVEL < ((SYSDATE - DATE'2023-01-24') / 7) + 1

Related

How to get entries from a SQL table for past 22 weekdays/ 8 Weekends

I want to get entries from the table for the past 22 weekdays or 8 weekends
I have one created_on column which serves as source of information for finding when the entry was created
I use it to extract dow and filter output by weekday and weekend
But I'm not able to figure out how can I get data for exactly 22 weekdays?
An example query would really help
You can use BETWEEN.
Example,
WHERE tablename.created_on BETWEEN "01.06.2022" AND "15.06.2022"
You could use the 'DY' date mask to filter the days and CONNECT BY LEVEL to get the date counts
SELECT t.something, t.created_on
FROM your_table t
WHERE 22 = (SELECT COUNT(1)
FROM DUAL
WHERE TO_CHAR(TRUNC(t.created_on ,'DD') + LEVEL - 1, 'DY') NOT IN ('SAT', 'SUN')
CONNECT BY LEVEL <= SYSDATE - TRUNC(t.created_on,'DD') + 1);

Calculate current quarter Fiscal_start date based off 13 week (91 day) rolling quarters

I am looking for a SQL solution to work in Oracle Developer SQL that will calculate the current quarters start and end date based off of sysdate and that the Fiscal calendar starts on Feb 1, 2020. Each quarter is a Fixed 13 weeks (91 days) -- NOT 3 months, therefore each year has a slightly different fiscal calendar.
Code will be uploaded to automated reporting and do not want to adjust it each year.
Current year fiscal calendar attached as sample explanation.
Current Fiscal Calendar
I started to head down this road but got lost when i realized the start date wasnt able to be correct in this format.
End solution would be used for a where clause to determine reporting date range such as
( Where Report_Date between Modified_Quarter_Start and sysdate )
select trunc(add_months(add_months(trunc(sysdate,'Y') -1 ,
to_number(to_char(sysdate,'Q')) * 3),-1),'MM')
start_date,trunc(add_months(add_months(trunc(sysdate,'Y') -1 ,
to_number(to_char(sysdate,'Q')) * 3),+2),'MM')-1 Endd_date,
add_months(trunc(sysdate,'Y') -1 ,to_number(to_char(sysdate,'Q')) * 3) end_date ,
to_char(sysdate,'YYYY - q') qtr from dual
Greatly appreciate any assistance.
Posting incase someone else runs into the same predicement in the future.
After reading a mention of a case statement for fixed days in another thread, i thought of this solution.
Select Trunc(sysdate - to_date('02-Feb-2019')),
Case When Trunc(sysdate - to_date('02-Feb-2019')) > 91 Then to_date('02-Feb-2019') +
Round( to_number(Trunc(sysdate - to_date('02-Feb-2019'))/91),0) * 91
Else null End Current_Quarter_Start,
Case When Trunc(sysdate - to_date('02-Feb-2019')) > 91 Then to_date('02-Feb-2019') +
( (Round( to_number(Trunc(sysdate - to_date('02-Feb-2019'))/91),0) +1 )* 91)-1 Else null End Current_Quarter_End From Dual

How to pull previous month data for in January

I have an embedded query that I use to pull previous month data. Everything has been working fine until this month (January). My code looks like this:
(MONTH(CURRENT DATE)-1) = MONTH(TSTAMP)
I have it setup this way because I have a timestamp in my data that I base the query off of. This usually works like a charm, but it's not working this month and I think it's because of the new year. How does this function work when dealing with a different year? Is there a way to write it into the query so I don't have to worry about a change in year?
You can do this by using the year, like this:
YEAR(CURRENT DATE) * 12 + MONTH(CURRENT DATE) - 1 = YEAR(TSTAMP) * 12 + MONTH(TSTAMP)
This, in essence, converts the dates into months since time 0 -- so the -1 makes sense.
The proper way to do this is with a range query (one with an exclusive upper-bound, <, too), so that the db is free to us an index if one is available.
The first of the month can be retrieved pretty easily via:
CURRENT_DATE - (DAY(CURRENT_DATE) - 1) DAYS
(Subtract the difference in days between the current date and the start of the month)
This gives a wonderful upper-bound condition for the query:
WHERE tStamp < CURRENT_DATE - (DAY(CURRENT_DATE) - 1) DAYS
(Get everything before the start of the current month).
However, since we're really only interested in the previous month, we also need to limit the lower bound. Well that's everything since, or on, the start of that month... and since we can already get the start of the current month:
WHERE tStamp >= CURRENT_DATE - (DAY(CURRENT_DATE) - 1) DAYS + 1 MONTH
AND tStamp < CURRENT_DATE - (DAY(CURRENT_DATE) - 1) DAYS
There's a related way to do this, supposing you have a calendar table with appropriate indices. If you have a minimal table with these columns:
calendarDate - DATE
year - INTEGER
month - INTEGER
dayOfMonth - INTEGER
... you can use this table to get the relevant values instead:
WHERE tStamp >= (SELECT calendarDate
FROM calendarTable
WHERE year = YEAR(CURRENT_DATE - 1 MONTH)
AND month = MONTH(CURRENT_DATE - 1 MONTH)
AND dayOfMonth = 1)
AND tStamp < (SELECT calendarDate
FROM calendarTable
WHERE year = YEAR(CURRENT_DATE)
AND month = MONTH(CURRENT_DATE)
AND dayOfMonth = 1)
(there's a couple of different forms of this, but this one looks pretty simple)

SQL excluding weekend days from date pull

I am trying to extract dates which are 2 days after the current date, unless in the case that two days after the current date would be a weekend, in which case I am lookig for dates that are 4 days after the current date. I am using Data Studio and none of the sql functions (like dateadd) work on it.
I know I can get the dates I want using this pull:
select event_end_date from mdmins11.contract where event_end_date between (current date +1.9 days) and (current date + 2.1 days);
But this does not exclude the weekends. I was trying to do it mathematically as such:
select event_end_date
case(
when (current date + 2 days) = ('2014-09-06' + (7 days * int)) or ('2014-09-07' + (7 days * int))
then set #w = (current date + 4 days)
else set #w = (current date + 2 days)
)
from mdmins11.contract where event_end_date = #w;
Here I am trying to find the dates that are after Saturdays and Sundays in case (current date + 2) lands on a weekend. This is not working at all. Does anyone have any idea how to do this on sql without using the functions? I appriciate that my date formatting might be wrong but I am finding it hard to see the solution.
I found the answer! For anyone who is in the same situation:
select dayofweek(current date) as day_number,
event_end_date
from mdmins11.contract
where (dayofweek(event_end_date) = 6 /*Friday*/
and date(event_end_date) < current_date + 3 days
and date(event_end_date) >= current_date)
or (dayofweek(event_end_date) in (1,2,3,4,5,7) /*All other days of week*/
and date(event_end_date) < current_date + 2 days
and date(event_end_date) >= current_date)
;
If you happen to have a list of holiday dates, you can also incorporate this in.

Finding the days of the week within a date range using oracle SQL

Suppose the following table structure:
Event:
id: integer
start_date: datetime
end_date: datetime
Is there a way to query all of the events that fall on a particular day of the week? For example, I would like to find a query that would find every event that falls on a Monday. Figuring out if the start_date or end_date falls on a Monday, but I'm not sure how to find out for the dates between.
Pure SQL is preferred since there is a bias against stored procedures here, and we're calling this from a Rails context which from what I understand does not handle stored procedures as well.
SELECT *
FROM event
WHERE EXISTS
(
SELECT 1
FROM dual
WHERE MOD(start_date - TO_DATE(1, 'J') + level - 1, 7) = 6
CONNECT BY
level <= end_date - start_date + 1
)
The subquery iterates all days from start_date to end_date, checks each day, and if it's a Monday, returns 1.
You can easily extend this query for more complex conditions: check whether an event falls on ANY Monday OR Friday 13th, for instance:
SELECT *
FROM event
WHERE EXISTS (
SELECT 1
FROM dual
WHERE MOD(start_date - TO_DATE(1, 'J') + level - 1, 7) = 6
OR (MOD(start_date - TO_DATE(1, 'J') + level - 1, 7) = 3 AND TO_CHAR(start_date + level - 1, 'DD') = '13')
CONNECT BY
level <= end_date - start_date + 1
)
Note that I use MOD(start_date - TO_DATE(1, 'J') + level - 1, 7) instead of TO_CHAR('D'). This is because TO_CHAR('D') is affected by NLS_TERRITORY and should not be used for checking for a certain day of week.
This query does not use any indexes and always performs a full table scan. But this is not an issue in this specific case, as it's highly probable that a given interval will contain a Monday.
Even if the intervals are 1 day long, the index will return 14% of values, if intervals are longer, even more.
Since INDEX SCAN would be inefficient in this case, and the inner subquery is very fast (it uses in-memory FAST DUAL access method), this, I think, will be an optimal method, both by efficiency and extensibility.
See the entry in my blog for more detail:
Checking event dates
This should do it more simply:
select *
from event
where 2 between to_number(trim(to_char(start_date,'D')))
and to_number(trim(to_char(end_date,'D')))
or (end_date - start_date) > 6