SQL Query for last year 30 days in Oracle - sql

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

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.

I have date function to create for entering date and to create random time i want to combine both of them

hi dear i have a query which create dates for specic lenght which is mentioned and a query which is create random time want to join both for date generating is this
It create dates for specific lenght and insert into table query is
insert into calendar_date2
with dates as (
select date'2019-12-31'+level dt
from dual
connect by level <= 365
)
select dt,
case
when to_char(dt, 'fmday') in ('sunday')
then 'N' else 'Y'
end
from dates
;
what it will do is enter CALENDER_DATE2 all dates mentioned on connect by level
2nd query is to genrate random time
with test (start_date) as
(select to_date('01.01.2021 07:45', 'dd.mm.yyyy hh24:mi') from dual
)
select start_date + round(dbms_random.value(1, 15)) / (24 * 60) result
from test
connect by level <= 15
I want to write both query work together what i wrote which is not working for me that is
insert into calendar_date2
with dates as (
select dates(to_date('01.01.2020 07:45', 'dd.mm.yyyy hh24:mi'))+level dt
from dual
select start_date + round(dbms_random.value(1, 15)) / (24 * 60) result
from test
connect by level <= 365
)
select dt,
case
when to_char(dt, 'fmday') in ('sunday')
then 'N' else 'Y'
end
from dates
;
i write this but it not worked for me please help struck here from past many hours
You can use:
insert into calendar_date2 (dt, is_workday )
SELECT dt,
DECODE( TRUNC( dt ) - TRUNC( dt, 'IW' ), 6, 'N', 'Y' )
FROM (
SELECT DATE '2020-01-01' + FLOOR( (LEVEL - 1) / 15 )
+ FLOOR(DBMS_RANDOM.VALUE(1, 16)) * INTERVAL '1' MINUTE
AS dt
FROM DUAL
CONNECT BY
DATE '2020-01-01' + FLOOR( (LEVEL - 1) / 15 )
< ADD_MONTHS( DATE '2020-01-01', 12 )
)
Which for the table:
dt DATE
NOT NULL,
is_workday CHAR(1)
NOT NULL
CONSTRAINT calendar_date2__iwd__chk CHECK ( is_workday IN ( 'Y', 'N' ) )
);
Gives you 15 rows for each day of 2020 each with random offset between 1 and 15 minutes from midnight.
Note:
The number of days in the year is calculated so you do not need to account for leap years.
Use FLOOR( DBMS_RANDOM.VALUE( 1, 16 ) ) rather than ROUND( DBMS_RANDOM.VALUE( 1, 15 ) ) as the first will give you an even spread of values whereas the second will only give a value 1 when the random value is between 1 and 1.5 and a value of 15 between 14.5 and 15 which will both occur with only half the frequency as the rest of the numbers and would give you an unbalanced distribution.
You can calculate the day of the week by comparing the start of the day TRUNC( dt ) to the start of the ISO week TRUNC( dt, 'IW' ). This has the benefit that it is independent of the NLS settings and will work in all languages rather than just being limited to English speaking territories.
db<>fiddle here

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

get first day of a given week number in oracle

Please help to derive first day of a given week_no in oracle not from given date.
You can try following query:-
SELECT NEXT_DAY(MAX(d), 'SUN') REQUESTED_SUN
FROM (SELECT TO_DATE('01-01-2015', 'DD-MM-YYYY') + (ROWNUM-1) d FROM DUAL CONNECT BY LEVEL <= 366)
WHERE TO_CHAR(d, 'WW') = Your_Desired_WEEK_NO-1;
This might be helpful to you.
Use this query
Select TRUNC (Trunc(sysdate,'yyyy')+(:num-1)*7,'IW') from duaL;
:num is number of week from year 2015, or put year what you need instead of sysdate.
You can use this function to get the date of the ISO week:
CREATE FUNCTION TO_ISO_WEEK_DATE(
week NUMBER,
year NUMBER
) RETURN DATE DETERMINISTIC
IS
BEGIN
RETURN NEXT_DAY(
TO_DATE( TO_CHAR( year, '0000' ) || '0104', 'YYYYMMDD' )
- INTERVAL '7' DAY, 'MONDAY'
)
+ ( week - 1 ) * 7;
END TO_ISO_WEEK_DATE;
/