get previous week date in select query - sql

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' )

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.

ORACLE CONVERT CHAR FORMAT "YYYY-WW" TO DATE FORMAT

I am trying to convert char date format "YYYY-WW" in ORACLE to date for calculating by week in year but have a trouble with error message format code cannot appear in date input format
If i write
TO_DATE(TO_CHAR(TO_DATE('1970-01-01 07:00:00', 'YYYY-MM-DD HH:MI:SS'),'YYYY-MM'),'YYYY-MM')
It will be normal
But if i write
TO_DATE(TO_CHAR(TO_DATE('1970-01-01 07:00:00', 'YYYY-MM-DD HH:MI:SS'),'YYYY-WW'),'YYYY-WW')
The message error format code cannot appear in date input format appear. I don't have no idea how to convert it right
Thanks for any advice
You can use:
TO_DATE( year || '-01-01', 'YYYY-MM-DD' ) + INTERVAL '7' DAY * (week - 1)
For your code:
SELECT TO_DATE( TO_CHAR(dt, 'YYYY') || '-01-01', 'YYYY-MM-DD' )
+ INTERVAL '7' DAY * (TO_CHAR(dt, 'WW') - 1) AS week_start
FROM (
SELECT TO_DATE('1970-01-01 07:00:00', 'YYYY-MM-DD HH24:MI:SS') AS dt
FROM DUAL
);
If you want to use ISO weeks (which always start on a Monday, rather than counting from the 1st January as WW does) then:
TRUNC( TO_DATE( iso_year || '-01-04', 'YYYY-MM-DD' ), 'IW')
+ INTERVAL '7' DAY * (iso_week - 1)
db<>fiddle here
You can have a look at this for guidance. https://asktom.oracle.com/pls/apex/asktom.search?tag=week-of-year-in-sql-confusing
There isn't a built-in way to convert a week number back to a date, as the result would be a bit arbitrary - which of the (up to) seven days in the week should it return?
If you want the first day of the week, as would be found by truncating the original date to WW precision (which is based on the day of the week of the first day of the year - docs), then you can reverse that process by getting the first day of the year and then adding the number of days in the specified number of weeks.
To do that you need to split your string into the two component parts:
substr('1970-01', 1, 4)
substr('1970-01', 6, 2)
and convert the first part to a date:
to_date(substr('1970-01', 1, 4) || '-01-01', 'YYYY-MM-DD')
and the second part to a number of weeks:
to_number(substr('1970-01', 6, 2))
then subtract one from that number of weeks, multiply by seven, and add it to the base date:
select to_date(substr('1970-01', 1, 4) || '-01-01', 'YYYY-MM-DD')
+ (7 * (to_number(substr('1970-01', 6, 2)) - 1))
from dual
1970-01-01
It's bit hard to tell that has been adjusted, so trying with today's date, which is week 30 of 2022:
select to_date(substr('2022-30', 1, 4) || '-01-01', 'YYYY-MM-DD')
+ (7 * (to_number(substr('2022-30', 6, 2)) - 1))
from dual
2022-07-23
which matches what trunc(sysdate, 'WW') gives.
db<>fiddle
If you want a specific day of the week then you'll need to figure out how much to adjust that, based on what day of the week the first day of the year was.
Select
Extract (year from TO_DATE('1970-01-01 07:00:00', 'YYYY-MM-DD HH:MI:SS')) || '-' ||
TO_CHAR(TO_DATE('1970-01-01 07:00:00', 'YYYY-MM-DD HH:MI:SS'),'WW')
From dual
If you are starting from a string like '1970-01-01 07:00:00' and want the first day of the corresponding week, you don't need to convert to and from strings and dates repeatedly; you just need to convert that string to a date once, and truncate it:
trunc(to_date('1970-01-01 07:00:00'), 'WW')
or if you want the ISO week:
trunc(to_date('1970-01-01 07:00:00'), 'IW')
For your example that would give:
select to_date('1970-01-01 07:00:00') as dt,
trunc(to_date('1970-01-01 07:00:00'), 'WW') as ww,
trunc(to_date('1970-01-01 07:00:00'), 'IW') as iw
from dual;
DT
WW
IW
1970-01-01 07:00:00
1970-01-01 00:00:00
1969-12-29 00:00:00
And for today it would give:
select to_date('2022-07-28 07:00:00') as dt,
trunc(to_date('2022-07-28 07:00:00'), 'WW') as ww,
trunc(to_date('2022-07-28 07:00:00'), 'IW') as iw
from dual;
DT
WW
IW
2022-07-28 07:00:00
2022-07-23 00:00:00
2022-07-25 00:00:00
db<>fiddle

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

How to determine week of a quarter in Oracle query

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).

SQL Query for last year 30 days in Oracle

In Oracle, I would like to get data for:
last 30 days from the current date and
also for the last 30 days from the (current date - 365) which is previous year
I was able to do so successfully for the first item:
WHERE CREATE_TIMESTAMP > SYSDATE - 30
But not sure how to achieve that for the second item. I tried:
WHERE CREATE_TIMESTAMP BETWEEN ((SYSDATE - 395) AND (SYSDATE - 365))
Moreover, my CREATE_TIMESTAMP is a timestamp column.
If you are just trying to get a list of those dates which are within the last 30 days of either this year or the same dates of last year then:
SELECT TRUNC(SYSDATE) - LEVEL + 1
FROM DUAL
CONNECT BY LEVEL <= 30
UNION ALL
SELECT ADD_MONTHS( TRUNC(SYSDATE) - LEVEL + 1, -12 )
FROM DUAL
CONNECT BY LEVEL <= 30
If you are trying to restrict the data from a query to only those dates then:
WHERE ( date_column BETWEEN sysdate - 30
AND sysdate
OR date_column BETWEEN ADD_MONTHS( sysdate, -12 ) - 30
AND ADD_MONTHS( sysdate, -12 )
)
Or if you are particularly worried about the conversion from TIMESTAMP to a DATE in the above filter then you can use the function below:
CREATE OR REPLACE FUNCTION TS_ADD_MONTHS(
datetime TIMESTAMP,
months INT
) RETURN TIMESTAMP DETERMINISTIC
AS
p_date CONSTANT DATE := TRUNC( datetime );
BEGIN
RETURN CAST( ADD_MONTHS( p_date, months ) AS TIMESTAMP )
+ ( datetime - p_date );
END;
/
SHOW ERRORS;
Then in your query can use:
WHERE ( date_column BETWEEN SYSTIMESTAMP - INTERVAL '30' DAY
AND SYSTIMESTAMP
OR date_column BETWEEN TS_ADD_MONTHS( sysdate, -12 ) - INTERVAL '30' DAY
AND TS_ADD_MONTHS( sysdate, -12 )
)