I have this Calendar Date table (see image below), but would like to learn the expressions behind a few of the columns.
The columns I need to replicate are the last three columns: "WeekBegin", "WeekEnd", "DaysinWeek". Of course, I really just need help with one to get the rest, so "WeekBegin" is the main question.
For dates 1/1/2023-1/3/2023, it gives the "WeekBegin" as 1/1/2023, then for 1/4/2023-1/10/2023 it gives the "WeekBegin" as 1/4/2022. This is perfect, I just need to shift it to a Monday Begin. So the result would be 1/1/2023 is "WeekBegin" 1/1/2023, while 1/2/2023-1/8/2023 is "WeekBegin" 1/2/2023.
I can't figure out the right code to have this replicate across the years! I've tried the regular date formulas, but can't figure out this dynamic piece to NOT have the week go into the previous year for "WeekBegin".
You could take either the previous wednesday or start of the year whichever happens later. Before we do the math, we need to add one day to date (see dates+1 below) so that if today was a wednesday it would return today's date as beginning of the week
with cte(dates) as
(select current_date()-1 union all
select current_date()-2 union all
select current_date()-3 union all
select current_date()-4 union all
select current_date()-5 union all
select current_date()-6 union all
select current_date()-7 union all
select current_date()-8 union all
select current_date()-9 union all
select current_date()-10 union all
select current_date()-11 union all
select current_date()-12 union all
select current_date()-13 union all
select current_date()-14 union all
select current_date()-15 union all
select current_date()-16 union all
select current_date()-17 union all
select current_date()-18 )
select *, greatest(previous_day(dates+1,'we'),trunc(dates,'month')) as week_begin
from cte
order by dates asc;
Before I start, a PSA about a couple of session parameters you will want to be aware of when working with dates. These will impact certain date functions.
If possible, I highly recommend setting these values account-wide for consistency. The defaults are ISO standard, but also, in my experience, the least likely to be used by (US-based) companies.
WEEK_START
Type: Session — Can be set for Account » User » Session
Data Type: Number
Description:
Specifies the first day of the week (used by week-related date functions).
Values:
0: Legacy Snowflake behavior is used (i.e. ISO-like semantics).
1 (Monday) to 7 (Sunday): All the week-related functions use weeks
that start on the specified day of the week.
Default:
0 (i.e. legacy Snowflake behavior)
and...
WEEK_OF_YEAR_POLICY
Type: Session — Can be set for Account » User » Session
Data Type: Number
Description: Specifies how the weeks in a given year are computed.
Values:
0: The semantics used are equivalent to the ISO semantics, in which a week belongs to a given year if at least 4 days of that week are in that year.
1: January 1 is included in the first week of the year and December 31 is included in the last week of the year.
Default: 0 (i.e. ISO-like behavior)
With that out of the way...
This expression should work to make sure the weekstart date, derived from a calendar date, starts on the first day of the year, if the weekstart returns as in the previous year.
iff( -- is the year extracted from the derived weekstart date the same as the year extracted from the date?
year(date_trunc(week, date_key)) = year(date_key)
-- if so, truncate date to the start of the week
, date_trunc(week, date_key)
-- if not, the fist day of the week must be the first day of the calendar year.
, date_trunc(year, date_key)
) dk_weekstart_calendar_year
sample query w/ results across the first 10 days of a few years (highlighed results are the standard datetrunc(week, ) values, desired results will to the right in the table).
with sample_dates as (
select
array_construct(
'2019-01-01',
'2019-01-02',
'2019-01-03',
'2019-01-04',
'2019-01-05',
'2019-01-06',
'2019-01-07',
'2019-01-08',
'2019-01-09',
'2020-01-01',
'2020-01-02',
'2020-01-03',
'2020-01-04',
'2020-01-05',
'2020-01-06',
'2020-01-07',
'2020-01-08',
'2020-01-09',
'2021-01-01',
'2021-01-02',
'2021-01-03',
'2021-01-04',
'2021-01-05',
'2021-01-06',
'2021-01-07',
'2021-01-08',
'2021-01-09',
'2022-01-01',
'2022-01-02',
'2022-01-03',
'2022-01-04',
'2022-01-05',
'2022-01-06',
'2022-01-07',
'2022-01-08',
'2022-01-09',
'2023-01-01',
'2023-01-02',
'2023-01-03',
'2023-01-04',
'2023-01-05',
'2023-01-06',
'2023-01-07',
'2023-01-08',
'2023-01-09'
) as date_array
)
select
f.value::date as date_key
, dayname(date_key) as dk_name
, dayofweek(date_key) as dk_dayofweek
, date_trunc(week, date_key) as dk_weekstart -- always, first day of the week as defined by week_start, *will go to previous year*
, dayname(dk_weekstart) as dk_weekstart_name --
, iff(
year(date_trunc(week, date_key)) = year(date_key) -- is the year extracted from the date of the weekstart the same as the year of date?
, date_trunc(week, date_key) -- if so, truncate date to the start of the week
, date_trunc(year, date_key) -- if not, the fist day of the week must be the first day of the calendar year.
) dk_weekstart_calendar_year
, dayname(dk_weekstart_calendar_year) as dk_weekstart_calendar_year_name
, last_day(date_key, 'week') as dk_weekend
, dayname(dk_weekend) as dk_weekend_name
from sample_dates e,
lateral flatten(e.date_array) f;
DATE_KEY
DK_NAME
DK_DAYOFWEEK
DK_WEEKSTART
DK_WEEKSTART_NAME
DK_WEEKSTART_CALENDAR_YEAR
DK_WEEKSTART_CALENDAR_YEAR_NAME
DK_WEEKEND
DK_WEEKEND_NAME
2019-01-01
Tue
2
2018-12-31
Mon
2019-01-01
Tue
2019-01-06
Sun
2019-01-02
Wed
3
2018-12-31
Mon
2019-01-01
Tue
2019-01-06
Sun
2019-01-03
Thu
4
2018-12-31
Mon
2019-01-01
Tue
2019-01-06
Sun
2019-01-04
Fri
5
2018-12-31
Mon
2019-01-01
Tue
2019-01-06
Sun
2019-01-05
Sat
6
2018-12-31
Mon
2019-01-01
Tue
2019-01-06
Sun
2019-01-06
Sun
7
2018-12-31
Mon
2019-01-01
Tue
2019-01-06
Sun
2019-01-07
Mon
1
2019-01-07
Mon
2019-01-07
Mon
2019-01-13
Sun
2019-01-08
Tue
2
2019-01-07
Mon
2019-01-07
Mon
2019-01-13
Sun
2019-01-09
Wed
3
2019-01-07
Mon
2019-01-07
Mon
2019-01-13
Sun
2020-01-01
Wed
3
2019-12-30
Mon
2020-01-01
Wed
2020-01-05
Sun
2020-01-02
Thu
4
2019-12-30
Mon
2020-01-01
Wed
2020-01-05
Sun
2020-01-03
Fri
5
2019-12-30
Mon
2020-01-01
Wed
2020-01-05
Sun
2020-01-04
Sat
6
2019-12-30
Mon
2020-01-01
Wed
2020-01-05
Sun
2020-01-05
Sun
7
2019-12-30
Mon
2020-01-01
Wed
2020-01-05
Sun
2020-01-06
Mon
1
2020-01-06
Mon
2020-01-06
Mon
2020-01-12
Sun
2020-01-07
Tue
2
2020-01-06
Mon
2020-01-06
Mon
2020-01-12
Sun
2020-01-08
Wed
3
2020-01-06
Mon
2020-01-06
Mon
2020-01-12
Sun
2020-01-09
Thu
4
2020-01-06
Mon
2020-01-06
Mon
2020-01-12
Sun
2021-01-01
Fri
5
2020-12-28
Mon
2021-01-01
Fri
2021-01-03
Sun
2021-01-02
Sat
6
2020-12-28
Mon
2021-01-01
Fri
2021-01-03
Sun
2021-01-03
Sun
7
2020-12-28
Mon
2021-01-01
Fri
2021-01-03
Sun
2021-01-04
Mon
1
2021-01-04
Mon
2021-01-04
Mon
2021-01-10
Sun
2021-01-05
Tue
2
2021-01-04
Mon
2021-01-04
Mon
2021-01-10
Sun
2021-01-06
Wed
3
2021-01-04
Mon
2021-01-04
Mon
2021-01-10
Sun
2021-01-07
Thu
4
2021-01-04
Mon
2021-01-04
Mon
2021-01-10
Sun
2021-01-08
Fri
5
2021-01-04
Mon
2021-01-04
Mon
2021-01-10
Sun
2021-01-09
Sat
6
2021-01-04
Mon
2021-01-04
Mon
2021-01-10
Sun
2022-01-01
Sat
6
2021-12-27
Mon
2022-01-01
Sat
2022-01-02
Sun
2022-01-02
Sun
7
2021-12-27
Mon
2022-01-01
Sat
2022-01-02
Sun
2022-01-03
Mon
1
2022-01-03
Mon
2022-01-03
Mon
2022-01-09
Sun
2022-01-04
Tue
2
2022-01-03
Mon
2022-01-03
Mon
2022-01-09
Sun
2022-01-05
Wed
3
2022-01-03
Mon
2022-01-03
Mon
2022-01-09
Sun
2022-01-06
Thu
4
2022-01-03
Mon
2022-01-03
Mon
2022-01-09
Sun
2022-01-07
Fri
5
2022-01-03
Mon
2022-01-03
Mon
2022-01-09
Sun
2022-01-08
Sat
6
2022-01-03
Mon
2022-01-03
Mon
2022-01-09
Sun
2022-01-09
Sun
7
2022-01-03
Mon
2022-01-03
Mon
2022-01-09
Sun
2023-01-01
Sun
7
2022-12-26
Mon
2023-01-01
Sun
2023-01-01
Sun
2023-01-02
Mon
1
2023-01-02
Mon
2023-01-02
Mon
2023-01-08
Sun
2023-01-03
Tue
2
2023-01-02
Mon
2023-01-02
Mon
2023-01-08
Sun
2023-01-04
Wed
3
2023-01-02
Mon
2023-01-02
Mon
2023-01-08
Sun
2023-01-05
Thu
4
2023-01-02
Mon
2023-01-02
Mon
2023-01-08
Sun
2023-01-06
Fri
5
2023-01-02
Mon
2023-01-02
Mon
2023-01-08
Sun
2023-01-07
Sat
6
2023-01-02
Mon
2023-01-02
Mon
2023-01-08
Sun
2023-01-08
Sun
7
2023-01-02
Mon
2023-01-02
Mon
2023-01-08
Sun
2023-01-09
Mon
1
2023-01-09
Mon
2023-01-09
Mon
2023-01-15
Sun
Related
I am trying to do a case statement within the where clause in snowflake but I’m not quite sure how should I go about doing it.
What I’m trying to do is, if my current month is Jan, then the where clause for date is between start of previous year and today. If not, the where clause for date would be between start of current year and today.
WHERE
CASE MONTH(CURRENT_DATE()) = 1 THEN DATE BETWEEN DATE_TRUNC(‘YEAR’, DATEADD(YEAR, -1, CURRENT_DATE())) AND CURRENT_DATE()
CASE MONTH(CURRENT_DATE()) != 1 THEN DATE BETWEEN DATE_TRUNC(‘YEAR’, CURRENT_DATE()) AND CURRENT_DATE()
END
Appreciate any help on this!
Use a CASE expression that returns -1 if the current month is January or 0 for any other month, so that you can get with DATEADD() a date of the previous or the current year to use in DATE_TRUNC():
WHERE DATE BETWEEN
DATE_TRUNC('YEAR', DATEADD(YEAR, CASE WHEN MONTH(CURRENT_DATE()) = 1 THEN -1 ELSE 0 END, CURRENT_DATE()))
AND
CURRENT_DATE()
I suspect that you don't even need to use CASE here:
WHERE
(MONTH(CURRENT_DATE()) = 1 AND
DATE BETWEEN DATE_TRUNC(‘YEAR’, DATEADD(YEAR, -1, CURRENT_DATE())) AND
CURRENT_DATE()) OR
(MONTH(CURRENT_DATE()) != 1 AND
DATE BETWEEN DATE_TRUNC(‘YEAR’, CURRENT_DATE()) AND CURRENT_DATE())
So the other answers are quite good, but... the answer can be even simpler
Making a little table to brake down what is happening.
select
row_number() over (order by null) - 1 as rn,
dateadd('day', rn * 5, date_trunc('year',current_date())) as pretend_current_date,
DATEADD(YEAR, -1, pretend_current_date) as pcd_sub1,
month(pretend_current_date) as pcd_month,
DATE_TRUNC(year, iff(pcd_month = 1, pcd_sub1, pretend_current_date)) as _from,
pretend_current_date as _to
from table(generator(ROWCOUNT => 30))
order by rn;
this shows:
RN
PRETEND_CURRENT_DATE
PCD_SUB1
PCD_MONTH
_FROM
_TO
0
2022-01-01
2021-01-01
1
2021-01-01
2022-01-01
1
2022-01-06
2021-01-06
1
2021-01-01
2022-01-06
2
2022-01-11
2021-01-11
1
2021-01-01
2022-01-11
3
2022-01-16
2021-01-16
1
2021-01-01
2022-01-16
4
2022-01-21
2021-01-21
1
2021-01-01
2022-01-21
5
2022-01-26
2021-01-26
1
2021-01-01
2022-01-26
6
2022-01-31
2021-01-31
1
2021-01-01
2022-01-31
7
2022-02-05
2021-02-05
2
2022-01-01
2022-02-05
8
2022-02-10
2021-02-10
2
2022-01-01
2022-02-10
9
2022-02-15
2021-02-15
2
2022-01-01
2022-02-15
10
2022-02-20
2021-02-20
2
2022-01-01
2022-02-20
11
2022-02-25
2021-02-25
2
2022-01-01
2022-02-25
12
2022-03-02
2021-03-02
3
2022-01-01
2022-03-02
13
2022-03-07
2021-03-07
3
2022-01-01
2022-03-07
14
2022-03-12
2021-03-12
3
2022-01-01
2022-03-12
15
2022-03-17
2021-03-17
3
2022-01-01
2022-03-17
16
2022-03-22
2021-03-22
3
2022-01-01
2022-03-22
17
2022-03-27
2021-03-27
3
2022-01-01
2022-03-27
18
2022-04-01
2021-04-01
4
2022-01-01
2022-04-01
19
2022-04-06
2021-04-06
4
2022-01-01
2022-04-06
20
2022-04-11
2021-04-11
4
2022-01-01
2022-04-11
21
2022-04-16
2021-04-16
4
2022-01-01
2022-04-16
22
2022-04-21
2021-04-21
4
2022-01-01
2022-04-21
23
2022-04-26
2021-04-26
4
2022-01-01
2022-04-26
24
2022-05-01
2021-05-01
5
2022-01-01
2022-05-01
25
2022-05-06
2021-05-06
5
2022-01-01
2022-05-06
26
2022-05-11
2021-05-11
5
2022-01-01
2022-05-11
27
2022-05-16
2021-05-16
5
2022-01-01
2022-05-16
28
2022-05-21
2021-05-21
5
2022-01-01
2022-05-21
29
2022-05-26
2021-05-26
5
2022-01-01
2022-05-26
Your logic is asking "is the current date in the month of January", at which point take the prior year, and then date truncate to the year, otherwise take the current date and truncate to the year. As the start of a BETWEEN test.
This is the same as getting the current date subtracting one month, and truncating this to year.
Thus there is no need for any IFF or CASE
WHERE date BETWEEN DATE_TRUNC(year, DATEADD(month,-1, CURRENT_DATE())) AND CURRENT_DATE()
and if you like to drop some paren's, CURRENT_DATE can be used if you leave it in upper case, thus it can even be smaller:
WHERE date BETWEEN DATE_TRUNC(year, DATEADD(month,-1, CURRENT_DATE)) AND CURRENT_DATE
I asked a question a couple weeks ago and I thought I asked the correct question, but I needed to modify it. I am posting a new question, however the previous post can be found here Thanks to #Serg for the quick reply to my previous question.
I've copied and pasted the question below. The modification to my question is, I assumed that the end date was sysdate, whereas I actually have an END_DATE column to work from. So, instead of count all files that were active each year until sysdate, I want to count all files there were active each year until END_DATE.
My previous question edited for my new question:
I have an Oracle table containing a number of active files. Each row is a file and has unique file #, issue date and end date
File #
ISSUE_DATE
END_DATE
1254
15-OCT-1997
11-NOV-2005
5245
22-MAY-2005
15-OCT-2008
7852
02-APR-2015
05-DEC-2023
9852
11-MAR-2021
22-OCT-2028
etc
I want to query a count of how many files were active each year between the year in which it was issued and the year of its end date. So, if it was issued in 2010, then I want to include that file in the count for each year since it was issued (2010, 2011, 2012, etc), up to its end date.
I'd like my end table to loo like:
Year
COUNT_OF_FILES
1997
20
1998
32
1999
55
2000
42
...
...
2019
130
2020
155
2021
151
2022
101
2023
98
2024
61
Just use simple generator:
Step 1: get all years for each record:
with
t(File#, ISSUE_DATE, END_DATE) as (
select 1254, to_date('15-OCT-1997','dd-mon-yyyy'),to_date('11-NOV-2005','dd-mon-yyyy') from dual union all
select 5245, to_date('22-MAY-2005','dd-mon-yyyy'),to_date('15-OCT-2008','dd-mon-yyyy') from dual union all
select 7852, to_date('02-APR-2015','dd-mon-yyyy'),to_date('05-DEC-2023','dd-mon-yyyy') from dual union all
select 9852, to_date('11-MAR-2021','dd-mon-yyyy'),to_date('22-OCT-2028','dd-mon-yyyy') from dual
)
select
t.*, yyyy
from t
cross apply(
select
extract(year from ISSUE_DATE)+level-1 as yyyy
from dual
connect by extract(year from ISSUE_DATE)+level-1<=extract(year from END_DATE)
) gen_years
this will return:
FILE# ISSUE_DATE END_DATE YYYY
---------- ------------------- ------------------- ----------
1254 1997-10-15 00:00:00 2005-11-11 00:00:00 1997
1254 1997-10-15 00:00:00 2005-11-11 00:00:00 1998
1254 1997-10-15 00:00:00 2005-11-11 00:00:00 1999
1254 1997-10-15 00:00:00 2005-11-11 00:00:00 2000
1254 1997-10-15 00:00:00 2005-11-11 00:00:00 2001
1254 1997-10-15 00:00:00 2005-11-11 00:00:00 2002
1254 1997-10-15 00:00:00 2005-11-11 00:00:00 2003
1254 1997-10-15 00:00:00 2005-11-11 00:00:00 2004
1254 1997-10-15 00:00:00 2005-11-11 00:00:00 2005
5245 2005-05-22 00:00:00 2008-10-15 00:00:00 2005
5245 2005-05-22 00:00:00 2008-10-15 00:00:00 2006
5245 2005-05-22 00:00:00 2008-10-15 00:00:00 2007
5245 2005-05-22 00:00:00 2008-10-15 00:00:00 2008
7852 2015-04-02 00:00:00 2023-12-05 00:00:00 2015
7852 2015-04-02 00:00:00 2023-12-05 00:00:00 2016
7852 2015-04-02 00:00:00 2023-12-05 00:00:00 2017
7852 2015-04-02 00:00:00 2023-12-05 00:00:00 2018
7852 2015-04-02 00:00:00 2023-12-05 00:00:00 2019
7852 2015-04-02 00:00:00 2023-12-05 00:00:00 2020
7852 2015-04-02 00:00:00 2023-12-05 00:00:00 2021
7852 2015-04-02 00:00:00 2023-12-05 00:00:00 2022
7852 2015-04-02 00:00:00 2023-12-05 00:00:00 2023
9852 2021-03-11 00:00:00 2028-10-22 00:00:00 2021
9852 2021-03-11 00:00:00 2028-10-22 00:00:00 2022
9852 2021-03-11 00:00:00 2028-10-22 00:00:00 2023
9852 2021-03-11 00:00:00 2028-10-22 00:00:00 2024
9852 2021-03-11 00:00:00 2028-10-22 00:00:00 2025
9852 2021-03-11 00:00:00 2028-10-22 00:00:00 2026
9852 2021-03-11 00:00:00 2028-10-22 00:00:00 2027
9852 2021-03-11 00:00:00 2028-10-22 00:00:00 2028
Then just aggregate them:
select
yyyy, count(*) cnt
from t
cross apply(
select
extract(year from ISSUE_DATE)+level-1 as yyyy
from dual
connect by extract(year from ISSUE_DATE)+level-1<=extract(year from END_DATE)
) gen_years
group by yyyy;
Full test case with test data:
with
t(File#, ISSUE_DATE, END_DATE) as (
select 1254, to_date('15-OCT-1997','dd-mon-yyyy'),to_date('11-NOV-2005','dd-mon-yyyy') from dual union all
select 5245, to_date('22-MAY-2005','dd-mon-yyyy'),to_date('15-OCT-2008','dd-mon-yyyy') from dual union all
select 7852, to_date('02-APR-2015','dd-mon-yyyy'),to_date('05-DEC-2023','dd-mon-yyyy') from dual union all
select 9852, to_date('11-MAR-2021','dd-mon-yyyy'),to_date('22-OCT-2028','dd-mon-yyyy') from dual
)
select
yyyy, count(*) cnt
from t
cross apply(
select
extract(year from ISSUE_DATE)+level-1 as yyyy
from dual
connect by extract(year from ISSUE_DATE)+level-1<=extract(year from END_DATE)
) gen_years
group by yyyy
order by yyyy;
Output:
YYYY CNT
---------- ----------
1997 1
1998 1
1999 1
2000 1
2001 1
2002 1
2003 1
2004 1
2005 2
2006 1
2007 1
2008 1
2015 1
2016 1
2017 1
2018 1
2019 1
2020 1
2021 2
2022 2
2023 2
2024 1
2025 1
2026 1
2027 1
2028 1
In our project we are maintaining a customers weekly spend. For that every week start we are resetting a customer limit.
Whenever customer doing a transaction we are updating his weekly spend by using the below query.
UPDATE SUMMARY
SET WEEKLIMIT = (
SELECT NVL (SUM (AMT / 100), 0)
FROM TRANSACTION
WHERE MOBILENO = :mobileNumber
AND TRUNC(TXNDT) BETWEEN (TRUNC (SYSTIMESTAMP, 'IW') - 1 ) AND TRUNC (SYSTIMESTAMP)
) WHERE MOBILENO = :mobileNumber
But the problem is customer who are doing transaction in sunday 00:00:01 to 01:00:00 hours, the above query updating the previous week limits
instead of current week.
SUMMARY
MOBILENUMBER AMT TXNDATE
0000000000 10000 26-12-2019 09:05:34
0000000000 10000 28-12-2019 11:05:34
0000000000 10000 29-12-2019 00:01:35
When I run this query in sunday first hour it returning
200
But from next hour
it returning only
100
Why the starting hour of week gives the previous week data.
Here is my NLS parameters
NLS_LANGUAGE ENGLISH
NLS_TERRITORY INDIA
NLS_CURRENCY Rs
NLS_ISO_CURRENCY INDIA
NLS_NUMERIC_CHARACTERS .,
NLS_CALENDAR GREGORIAN
NLS_DATE_FORMAT DD-MM-RR
NLS_DATE_LANGUAGE ENGLISH
NLS_SORT BINARY
NLS_TIME_FORMAT HH12:MI:SSXFF AM
NLS_TIMESTAMP_FORMAT DD-MM-RR HH12:MI:SSXFF AM
NLS_TIME_TZ_FORMAT HH12:MI:SSXFF AM TZR
NLS_TIMESTAMP_TZ_FORMAT DD-MM-RR HH12:MI:SSXFF AM TZR
NLS_DUAL_CURRENCY Rs
NLS_COMP BINARY
NLS_LENGTH_SEMANTICS BYTE
NLS_NCHAR_CONV_EXCP FALSE
Any help will be greatly appreciated!!!!
I believe your issue lies in your date manipulation. You are doing:
TRUNC(TXNDT) BETWEEN (TRUNC (SYSTIMESTAMP, 'IW') - 1 ) AND TRUNC (SYSTIMESTAMP)
and expecting that to make the start of the week a Sunday.
However, you haven't done this correctly - your logic says "take the Monday of the current week and subtract a day from it". If your day is a Sunday, that means it first gets the previous Monday and subtracts a day to make it the previous Sunday.
What you must first do is turn the Sunday into a Monday (by adding a day), truncate it to the first day of the week (Monday), and then subtract a day.
Which means your predicate should now look like:
TRUNC(TXNDT) BETWEEN (TRUNC (SYSDATE + 1, 'IW') - 1 ) AND TRUNC (SYSDATE)
Here's a test case demonstrating the difference:
WITH dts AS (SELECT TRUNC(SYSDATE, 'mm') - 1 + LEVEL dt
FROM dual
CONNECT BY LEVEL <= 13)
SELECT dt,
to_char(dt, 'Dy') day_of_week,
TRUNC(dt, 'iw') monday_start_week,
TRUNC(dt, 'iw') - 1 your_sunday_start_week,
TRUNC(dt + 1, 'iw') - 1 actual_sunday_start_week
FROM dts;
DT DAY_OF_WEEK MONDAY_START_WEEK YOUR_SUNDAY_START_WEEK ACTUAL_SUNDAY_START_WEEK
----------- ----------- ----------------- ---------------------- ------------------------
01/01/2020 Wed 30/12/2019 29/12/2019 29/12/2019
02/01/2020 Thu 30/12/2019 29/12/2019 29/12/2019
03/01/2020 Fri 30/12/2019 29/12/2019 29/12/2019
04/01/2020 Sat 30/12/2019 29/12/2019 29/12/2019
05/01/2020 Sun 30/12/2019 29/12/2019 05/01/2020 <-----
06/01/2020 Mon 06/01/2020 05/01/2020 05/01/2020
07/01/2020 Tue 06/01/2020 05/01/2020 05/01/2020
08/01/2020 Wed 06/01/2020 05/01/2020 05/01/2020
09/01/2020 Thu 06/01/2020 05/01/2020 05/01/2020
10/01/2020 Fri 06/01/2020 05/01/2020 05/01/2020
11/01/2020 Sat 06/01/2020 05/01/2020 05/01/2020
12/01/2020 Sun 06/01/2020 05/01/2020 12/01/2020 <-----
13/01/2020 Mon 13/01/2020 12/01/2020 12/01/2020
You can see that for the two Sunday rows, your version returns the previous Sunday, whereas the amended version returns the current Sunday.
I am writing a procedure and using dynamic cursor and SQL query which I am passing as string in V_SQL variable. Query is as following:
In the where condition, I'm passing date but the condition is that if it's month end and it falls on Friday, Saturday or sunday then reset it to Thursday. for instance, 30th June will be sunday so value passed to day_of_month in SQL query should be 27 i.e. day no from Thursday date.
Could you please help me whether writing separate function will be good and what code should I put for better performance and desired result.
V_SQL := 'SELECT B.FIN_ELEM, A.ORG_UNIT_ID, A.GL_ACCOUNT_ID, B.CMN_COA_ID, B.PROD1, B.PROD2, B.PROD3, '||
'A.AS_OF_DATE, SUM(CURRENT_BAL) AS CB_SUM, SUM(AVG_BAL) AS AB_SUM, B.FLAG1 FROM DAILYGL A, AL_LOOKUP B '||
'WHERE A.GL_ACCOUNT_ID = B.GL_ACCT ***AND DAY_OF_MONTH = '|| TO_DO_FUNCTION(V_RUN_DATE)***
' AND ROWNUM <=15 GROUP BY B.FIN_ELEM, A.ORG_UNIT_ID, A.GL_ACCOUNT_ID,B.CMN_COA_ID, B.PROD1, B.PROD2, B.PROD3';
DAY_OF_MONTH = '|| TO_DO_FUNCTION(V_RUN_DATE)
Desired result will be passed Thursday day number if last business day is in fri, sat or sunday.
You don't really need a function, you can achieve this with a case expression, something like:
AND DAY_OF_MONTH = case
when last_day(v_run_date) - v_run_date <= 3
and to_char(v_run_date, 'Dy', 'NLS_DATE_LANGUAGE=ENGLISH') in ('Fri', 'Sat', 'Sun')
and to_char(last_day(v_run_date), 'Dy', 'NLS_DATE_LANGUAGE=ENGLISH') in ('Fri', 'Sat', 'Sun')
then next_day(trunc(v_run_date, 'IW'), 'THURSDAY')
else v_run_date
end
That is, if v_run_date is within three days of the last day of the month, and v_run_date is a Friday, Saturday or Sunday, and the last day of the month is a Friday, Saturday or Sunday, then use the date of that week's Thursday.
Demo with dates generated for all of this year:
with cte (v_run_date) as (
select date '2018-12-31' + level
from dual
connect by level <= 365
)
select v_run_date,
to_char(v_run_date, 'Dy', 'NLS_DATE_LANGUAGE=ENGLISH') as dy,
case
when last_day(v_run_date) - v_run_date <= 3
and to_char(v_run_date, 'Dy', 'NLS_DATE_LANGUAGE=ENGLISH') in ('Fri', 'Sat', 'Sun')
and to_char(last_day(v_run_date), 'Dy', 'NLS_DATE_LANGUAGE=ENGLISH') in ('Fri', 'Sat', 'Sun')
then next_day(trunc(v_run_date, 'IW'), 'THURSDAY')
else v_run_date
end as adjusted_date
from cte;
V_RUN_DATE DY ADJUSTED_D
---------- ------------ ----------
2019-01-01 Tue 2019-01-01
2019-01-02 Wed 2019-01-02
...
2019-01-30 Wed 2019-01-30
2019-01-31 Thu 2019-01-31
2019-02-01 Fri 2019-02-01
2019-02-02 Sat 2019-02-02
...
2019-03-26 Tue 2019-03-26
2019-03-27 Wed 2019-03-27
2019-03-28 Thu 2019-03-28
2019-03-29 Fri 2019-03-28
2019-03-30 Sat 2019-03-28
2019-03-31 Sun 2019-03-28
2019-04-01 Mon 2019-04-01
...
2019-04-30 Tue 2019-04-30
2019-05-01 Wed 2019-05-01
...
2019-05-28 Tue 2019-05-28
2019-05-29 Wed 2019-05-29
2019-05-30 Thu 2019-05-30
2019-05-31 Fri 2019-05-30
2019-06-01 Sat 2019-06-01
2019-06-02 Sun 2019-06-02
...
2019-06-26 Wed 2019-06-26
2019-06-27 Thu 2019-06-27
2019-06-28 Fri 2019-06-27
2019-06-29 Sat 2019-06-27
2019-06-30 Sun 2019-06-27
2019-07-01 Mon 2019-07-01
...
2019-07-31 Wed 2019-07-31
2019-08-01 Thu 2019-08-01
...
2019-08-28 Wed 2019-08-28
2019-08-29 Thu 2019-08-29
2019-08-30 Fri 2019-08-29
2019-08-31 Sat 2019-08-29
2019-09-01 Sun 2019-09-01
...
2019-11-27 Wed 2019-11-27
2019-11-28 Thu 2019-11-28
2019-11-29 Fri 2019-11-28
2019-11-30 Sat 2019-11-28
2019-12-01 Sun 2019-12-01
...
db<>fiddle
As mentioned in a comment, your code doesn't appear to need to be dynamic; you can use static SQL for what you've shown, e.g.:
SELECT *
-- into ...
FROM (
SELECT B.FIN_ELEM, A.ORG_UNIT_ID, A.GL_ACCOUNT_ID,
B.CMN_COA_ID, B.PROD1, B.PROD2, B.PROD3, A.AS_OF_DATE,
SUM(CURRENT_BAL) AS CB_SUM, SUM(AVG_BAL) AS AB_SUM, B.FLAG1
FROM DAILYGL A
JOIN AL_LOOKUP B ON B.GL_ACCT = A.GL_ACCOUNT_ID
WHERE DAY_OF_MONTH = case
when last_day(v_run_date) - v_run_date <= 3
and to_char(v_run_date, 'Dy', 'NLS_DATE_LANGUAGE=ENGLISH') in ('Fri', 'Sat', 'Sun')
and to_char(last_day(v_run_date), 'Dy', 'NLS_DATE_LANGUAGE=ENGLISH') in ('Fri', 'Sat', 'Sun')
then next_day(trunc(v_run_date, 'IW'), 'THURSDAY')
else v_run_date
end
GROUP BY B.FIN_ELEM, A.ORG_UNIT_ID, A.GL_ACCOUNT_ID,B.CMN_COA_ID, B.PROD1, B.PROD2, B.PROD3
-- order by something...
)
WHERE ROWNUM <= 15;
I've also switched to ANSI joins but more importantly moved the main query into an inline view, and then applied the rownum filter to that; which will make no difference unless/until you add an order-by clause inside that inline view. Without it this, and your original, will get an indeterminate (not quite random, but similar effect) set of 15 rows back. If you order the inner query then you'll always get the first 15 rows according to your ordering criteria. From 12c you can use a row limiting clause instead of the subquery/rownum filter, but this approach will work in earlier versions.
Obviously you still need to either select that into variables or use it as a cursor query - whatever you had planned to do dynamically.
I hope this will solve your problem
V_RUN_DATE - (case when V_RUN_DATE > LAST_DAY (V_RUN_DATE)- 3 then V_RUN_DATE else
(case TO_CHAR(date V_RUN_DATE, 'DY') when 'FRI' then 1 when 'SAT' then 2 when 'SUN' then 3 else 0 end) end)
SELECT date1, To_Char(date1,'DY'), date2, To_Char(date2,'DY'), Trunc(date2-date1) Days
FROM Table_Name WHERE Trunc(date2-date1) > 0
This gives me the records which have the day-difference of more than 1 days. Now, I have to find out if a holiday or a saturday falls between the two dates. And if it does, then I have to exclude it from the 'day-difference' and show the results.
DATE 1 DAY1 DATE2 DAY2 DAY DIFFERENCE
19-MAY-12 SAT 21-MAY-12 MON 2
18-MAY-12 FRI 21-MAY-12 MON 3
18-MAY-12 FRI 21-MAY-12 MON 3
18-MAY-12 FRI 22-MAY-12 TUE 3
18-MAY-12 FRI 22-MAY-12 TUE 3
18-MAY-12 FRI 22-MAY-12 TUE 3
05-JUN-12 TUE 07-JUN-12 THU 2
05-JUN-12 TUE 07-JUN-12 THU 2
I tried this but there are a few hitches. I'm working on them.
SELECT transaction_date, TO_CHAR(transaction_date,'DY'), deposit_date, TO_CHAR(deposit_date,'DY'), trunc(deposit_date - transaction_date + 1) Total_Days,
trunc(deposit_date - transaction_date) -
(CASE
WHEN TO_CHAR(date1, 'DY') IN ('SAT')
OR date1 IN ('25-JAN-13', '27-MAR-13', '29-MAR-13', '01-APR-13', '24-APR-13', '09-AUG-13', '15-AUG-13', '30-SEP-13', '02-OCT-13'
, '13-OCT-13', '16-OCT-13', '03-NOV-13', '14-NOV-13', '17-NOV-13', '25-DEC-13')
THEN 1
WHEN TO_CHAR(date1, 'DY') NOT IN ('SAT')
THEN 0
END
+
CASE
WHEN TO_CHAR(date2, 'DY') IN ('SUN', 'SAT')
OR deposit_date IN ('25-JAN-13', '27-MAR-13', '29-MAR-13', '01-APR-13', '24-APR-13', '09-AUG-13', '15-AUG-13', '30-SEP-13', '02-OCT-13'
, '13-OCT-13', '16-OCT-13', '03-NOV-13', '14-NOV-13', '17-NOV-13', '25-DEC-13') THEN 1
ELSE 0
END
) BUSINESSDAYS
FROM Table_Name
DATE 1 DAY1 DATE2 DAY2 WORKING DAYS
19-MAY-12 SAT 21-MAY-12 MON 1
18-MAY-12 FRI 21-MAY-12 MON 3
18-MAY-12 FRI 21-MAY-12 MON 3
18-MAY-12 FRI 22-MAY-12 TUE 3
18-MAY-12 FRI 22-MAY-12 TUE 3
18-MAY-12 FRI 22-MAY-12 TUE 3
18-MAY-12 FRI 19-MAY-12 SAT 1