How to determine week of a quarter in Oracle query - sql

I want to find the Week of a Quarter from a sql date in Oracle.
I tried below query to find the year, quarter and week.
But the week field gives the 'Week of the month' not the 'Week of the quarter'
select to_char(sysdate, 'YYYY')|| '-Q' || to_char(sysdate, 'Q') || '-W' || >to_char(sysdate, 'w') as "Current Time"
from dual;
Above query returns '2016-Q2-W3' as the date falls in the 3rd week of the month.
Say sysdate is '17th June, 2016'
I am expecting result as
2016-Q2-W12
My Week range is (Sunday - Saturday)
Since the '17th June, 2016' comes under 12th week of the quarter, it should be W12.
Thanks in advance.

This will get the week (Sunday - Saturday) of the quarter:
SELECT TO_CHAR( SYSDATE, 'YYYY-"Q"Q-"W"' )
|| ( 7 + TRUNC( SYSDATE + 1, 'IW' ) - TRUNC( TRUNC( SYSDATE, 'Q' ) + 1, 'IW' ) ) / 7;
AS "Current Time"
FROM DUAL
Explanation:
You can find the Sunday which was either on-or-just-before a given date using NEXT_DAY( TRUNC( date_value ), 'SUNDAY' ) - 7 (which is dependant on the NLS_TERRITORY setting) or TRUNC( date_value + 1, 'IW' ) - 1 (which is shorter and not dependant on any settings).
TRUNC( date_value, 'Q' ) gives the date of the first day of the quarter containing the value date (i.e. either 1st January, 1st April, 1st July or 1st October).
Putting the two together, the Sunday on-or-just-before the first day of the quarter is given by TRUNC( TRUNC( date_value, 'Q' ) + 1, 'IW' ) - 1
Therefore, the number of days between the Sunday on-or-just-before the start of the quarter and the Sunday on-or-just-before a given date is: ( TRUNC( date_value + 1, 'IW' ) - 1 ) - ( TRUNC( TRUNC( date_value, 'Q' ) + 1, 'IW' ) - 1 ) - which can be simplified by cancelling the -1 terms.
The number of weeks difference is just that number divided by 7 (but gives a 0-indexed value and you want the week number of the quarter to be 1-indexed; you either add 1 week to the result or, prior to doing the division, add 7 days).

Related

Is there a way to use SYSDATE with a weekly date?

So I've been trying to fetch some daily data with SYSDATE on a date type YYYYMMDD as following:
SELECT dates, trunc(calendar_date, 'DD') calendar_dates, weekday_nbr
FROM db.date
WHERE dates BETWEEN to_char(TRUNC(SYSDATE)-2, 'YYYYMMDD') AND to_char(TRUNC(SYSDATE)-1, 'YYYYMMDD')
But now I'm trying to use the same but on a YYYY+MM+Week date type with not much success
I tried using:
SELECT T time, period, fiscal_week
FROM db.time
WHERE time BETWEEN to_char(TRUNC(SYSDATE)-2, 'W') AND to_char(TRUNC(SYSDATE)-1,'W')
With time as a 7 digit number, and period and fiscal week as a 2 digit number
Knowing that there's no way I can truncate such date type, how can TRUNC SYSDATE YYYY+MM+Week to get the data on the last 2 weeks?
Also I was thinking about maybe getting the totals from a set day and then dropping all but the last 2 weeks, but on the long run maybe that would be time consuming.
Knowing that there's no way I can truncate such date type, how can TRUNC SYSDATE YYYY+MM+Week to get the data on the last 2 weeks?
Assuming that your fiscal weeks are from Monday-Sunday then you can truncate to the start of the ISO week (which is always Midnight on Monday) and use that for the basis of the comparison:
SELECT *
FROM db.time
WHERE dates >= TRUNC(SYSDATE, 'IW') - INTERVAL '14' DAY
AND dates < TRUNC(SYSDATE, 'IW')
If you have a column that is for weeks then you should still use a DATE data type and add a CHECK constraint (and can use virtual columns to generate the week and the year):
CREATE TABLE time (
dt DATE
CHECK (dt = TRUNC(dt, 'IW')),
year NUMBER(4,0)
GENERATED ALWAYS AS (EXTRACT(YEAR FROM dt)),
month NUMBER(2,0)
GENERATED ALWAYS AS (EXTRACT(MONTH FROM dt)),
week NUMBER(1,0)
GENERATED ALWAYS AS (FLOOR((dt - TRUNC(dt, 'MM'))/7) + 1),
time VARCHAR2(7)
GENERATED ALWAYS AS (
CAST(
TO_CHAR(dt, 'YYYYMM')
|| (FLOOR((dt - TRUNC(dt, 'MM'))/7) + 1)
AS VARCHAR2(7)
)
)
-- ...
);
fiddle
Then you can use the logic above on the date column.
If you do not have a DATE column then you will need to convert your YYYYMMW number into a DATE and then use the logic above.
For example, if the logic for your fiscal weeks (which you have not described) is that the first week of each month starts on the first Monday of the month then you can convert the YYYYMMW number to a DATE using:
SELECT NEXT_DAY(
TO_DATE(SUBSTR(time, 1, 6), 'YYYYMM') - INTERVAL '1' DAY,
'MONDAY'
) + INTERVAL '7' DAY * (SUBSTR(time, 7, 1) - 1) AS week_start
FROM db.time
and then could use it to filter the table using:
SELECT *
FROM (
SELECT t.*,
NEXT_DAY(
TO_DATE(SUBSTR(time, 1, 6), 'YYYYMM') - INTERVAL '1' DAY,
'MONDAY'
) + INTERVAL '7' DAY * (SUBSTR(time, 7, 1) - 1) AS week_start
FROM db.time t
)
WHERE week_start >= TRUNC(SYSDATE, 'IW') - INTERVAL '14' DAY
AND week_start < TRUNC(SYSDATE, 'IW')
If you have different logic for calculating when fiscal weeks start then you will need to apply that logic to the conversion.

SQL oracle where date field = Last Friday

I have a where clause with a date field, I want it to find the latest Friday when this auto runs every Monday or Tuesday of the following week.
Currently:
WHERE b.CALENDAR_DATE = To_Date('2020-08-30', 'YYYY-MM-DD')
I want to find the latest Friday
You can use TRUNC( date_value, 'IW' ) (which is language independent) to truncate to the start of the ISO week, which is always a Monday, and then subtract 3 days. You will also want to add 2 days before truncating so that the filter is correct if SYSDATE is a Saturday or Sunday.
WHERE b.CALENDAR_DATE >= TRUNC( SYSDATE + INTERVAL '2' DAY, 'IW' ) - INTERVAL '3' DAY
AND b.CALENDAR_DATE < TRUNC( SYSDATE + INTERVAL '2' DAY, 'IW' ) - INTERVAL '2' DAY
(Note: dates can have a non-zero time component so if you want all dates on the last Friday then you will need a range of values rather than just comparing to last Friday at midnight.)
Or you can use NEXT_DAY( date_value - INTERVAL '8' DAY, 'FRIDAY' ) (which is language dependent):
WHERE b.CALENDAR_DATE >= NEXT_DAY( TRUNC( SYSDATE ) - INTERVAL '8' DAY, 'FRIDAY' )
AND b.CALENDAR_DATE < NEXT_DAY( TRUNC( SYSDATE ) - INTERVAL '8' DAY, 'FRIDAY' ) + INTERVAL '1' DAY
db<>fiddle here

previous friday of month

I am trying to write a query that will return the last friday of the previous month This is what I have so far.
SELECT sysdate
FROM DUAL
where (LAST_DAY( SYSDATE ) - INTERVAL '7' DAY,
'Friday' );
Consider:
select next_day(trunc(sysdate, 'month') - interval '8' day, 'Friday') from dual
Rationale:
trunc(sysdate, 'month') gives you the first day of the current month
we offset that value by 8 days so we can the the date 7 days before the end of the previous month
then, next_day() returns the next Friday after this date, that is the last Friday of the month
Try this:
SELECT NEXT_DAY( LAST_DAY( TO_DATE('01-' || TO_CHAR(SYSDATE, 'MON-YYYY')) - 1 ) - INTERVAL '7' DAY, 'FRIDAY' ) FROM DUAL;

Year-To-Date for Previous Years (Same Period Last Year)

I've got transaction data spanning 3 years and I would like to report on performance for the different years on a Year-To-Date basis.
The data is like:
REP_NAME : SECTION : AMOUNT : DATE
Mark : Sec1 : 2,000 : 01/10/2016
Jane : Sec2 : 1,400 : 02/12/2017
And so on...
What I want to do is write a query that will group all the amounts per year but being controlled by the current date of the current year.
2016 : 2017 : 2018
2500 : 2300 : 3400
Showing, YTD for 2018 is 3400 (i.e. between 01/01/2018 - 29/03/2018),
but same period the last two years (between 01/01/2017 - 29-03-2017)
and (01/01/2016 and 29/03/2016) the collection was 2,300 and 2,500 respectively.
I hope this is clear. I am using Oracle Database.
Thanks.
Just filter those values where either the date is in an earlier month of the year or the date is in the same month of the year on an earlier-or-same day of the month:
SELECT EXTRACT( YEAR FROM "DATE" ) AS year,
SUM( AMOUNT ) AS total_year_to_date_amount
FROM your_table
WHERE ( EXTRACT( MONTH FROM "DATE" ) < EXTRACT( MONTH FROM SYSDATE )
OR ( EXTRACT( MONTH FROM "DATE" ) = EXTRACT( MONTH FROM SYSDATE )
AND EXTRACT( DAY FROM "DATE" ) <= EXTRACT( DAY FROM SYSDATE )
)
)
AND "DATE" >= ADD_MONTHS( TRUNC( SYSDATE, 'YEAR' ), -24 )
GROUP BY EXTRACT( YEAR FROM "DATE" );
or you can explicitly set the ranges:
SELECT EXTRACT( YEAR FROM "DATE" ) AS year,
SUM( AMOUNT ) AS total_year_to_date_amount
FROM your_table
WHERE "DATE" BETWEEN TRUNC( SYSDATE, 'YEAR' )
AND SYSDATE
OR "DATE" BETWEEN ADD_MONTHS( TRUNC( SYSDATE, 'YEAR' ), -12 )
AND ADD_MONTHS( SYSDATE, -12 )
OR "DATE" BETWEEN ADD_MONTHS( TRUNC( SYSDATE, 'YEAR' ), -24 )
AND ADD_MONTHS( SYSDATE, -24 )
GROUP BY EXTRACT( YEAR FROM "DATE" );
(Note: the first query will include all results from the current day-of-the-year - i.e. 00:00 - 23:39 - whereas the second query only include results from the current day-of-the-year that are between 00:00 and the current time. You could give similar behaviour to the second query - one method is by using TRUNC( "DATE" ).)
You want to only consider certain days, i.e. January first till current day. So write a WHERE clause for this, looking at month and day, ignoring the year.
where to_char(date, 'mmdd') <= to_char(sysdate, 'mmdd')
Then you want sums per year. So group by year.
select
extract(year from date) as year,
sum(amount) as total
from mytable
where to_char(date, 'mmdd') <= to_char(sysdate, 'mmdd')
group by extract(year from date)
order by extract(year from date);

get previous week date in select query

I have below query which i am using for reporting in Cognos. I am doing reporting every week on Monday for the previous week from Monday till Sunday.
Currently this date is hardcoded but i want to make it dynamic so that everytime when i run this report on Monday i dont have to change the date for previous week.
Is it possible i can make DAY_DATE dynamic using something like sysdate in select query ?
select ID,NAME,DAY_DATE
from TEST_REPORT
WHERE DAY_DATE BETWEEN to_date ('20170904', 'YYYYMMDD') and to_date ('20170910', 'YYYYMMDD');
You can calculate start and end dates of previous week from the current date using TRUNC (date) function.
Let say you are running the query on monday 2017-09-11, then on friday 2017-09-15, and the query must always generate a report for previous week.
This query calculates a start date of the current week:
SELECT trunc( date '2017-09-11', 'IW' ) as x,
trunc( date '2017-09-15', 'IW' ) as y
from dual;
X Y
---------------- ----------------
2017/09/11 00:00 2017/09/11 00:00
To calculate a start date of the previous week, substract 1 day from the above dates, and use TRUCT again:
SELECT trunc( trunc( date '2017-09-11', 'IW' ) - 1, 'IW') as start_last_week,
trunc( trunc( date '2017-09-15', 'IW' ) - 1, 'IW') as start_last_week1
from dual;
START_LAST_WEEK START_LAST_WEEK1
---------------- ----------------
2017/09/04 00:00 2017/09/04 00:00
So, in your query just use this clause (date >= than a start of previous week and < that a start of current week):
WHERE DAY_DATE>= trunc( trunc( sysdate, 'IW' ) - 1, 'IW')
and DAY_DATE < trunc( sysdate, 'IW' )