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

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

Related

Query to find day number of the fiscal month in oracle SQL

The query should return the day number of the current fiscal month.Check the below query:
SELECT *
FROM GL_PERIODS
WHERE PERIOD_SET_NAME='Fiscal Year'
AND period_year =(SELECT DISTINCT period_year
FROM gl_periods
WHERE TRUNC (SYSDATE) BETWEEN start_date
AND end_date)
For period name : SEP-19, Start_Date is 8/26/2018. So, day number should be 16 for sysdate.
Sample Data:
period_set_name period_name start_date end_date entered_period_name
Fiscal Year AUG-19 7/29/2018 8/25/2018 AUG
Fiscal Year SEP-19 8/26/2018 9/22/2018 SEP
Fiscal Year OCT-19 9/23/2018 10/27/2018 OCT
Expected Output: 16
We can do arithmetic on dates. So today's Fiscal day number would be:
SELECT (trunc(sysdate) - start_date) + 1 as fiscal_day_no
FROM GL_PERIODS
WHERE PERIOD_SET_NAME='Fiscal Year'
AND period_year =(SELECT DISTINCT period_year
FROM gl_periods
WHERE TRUNC (SYSDATE) BETWEEN start_date
AND end_date)
This assumes you have no special requirement to handle e.g. weekends or public holidays.
Oracle Setup:
CREATE TABLE GL_PERIODS(
period_set_name VARCHAR2(20),
start_date DATE,
end_date DATE,
period_name CHAR(6) GENERATED ALWAYS AS ( CAST( TO_CHAR( end_date, 'MON-YY' ) AS CHAR(6) ) ) VIRTUAL,
entered_period_name CHAR(3) GENERATED ALWAYS AS ( CAST( TO_CHAR( end_date, 'MON' ) AS CHAR(3) ) ) VIRTUAL,
period_year NUMBER(4) GENERATED ALWAYS AS ( EXTRACT( YEAR FROM end_date ) ) VIRTUAL
);
INSERT INTO GL_PERIODS( period_set_name, start_date, end_date )
SELECT 'Fiscal Year', DATE '2018-07-29', DATE '2018-08-25' FROM DUAL UNION ALL
SELECT 'Fiscal Year', DATE '2018-08-26', DATE '2018-09-22' FROM DUAL UNION ALL
SELECT 'Fiscal Year', DATE '2018-09-23', DATE '2018-10-27' FROM DUAL;
Query:
SELECT TRUNC( SYSDATE - start_date + 1 ) AS fiscal_day_no
FROM GL_PERIODS
WHERE start_date <= SYSDATE
AND SYSDATE < END_DATE + INTERVAL '1' DAY;
Output:
| FISCAL_DAY_NO |
|---------------|
| 20 |

Returning a Date Range based on Day of the Month

Select *
From Orders
WHERE (
extract(day from sysdate)<=21
and
to_date(SCHEDULEDATE , 'yyyy/mm/dd') between
to_date((to_char(sysdate, 'YYYY')||'/'||cast((extract(month from sysdate)-1)as char)||'/22'),'yyyy/mm/dd') and to_date((to_char(sysdate,'YYYY')||'/'||cast((extract(month from sysdate))as char)||'/21'),'yyyy/mm/dd')
)
or
(
extract(day from sysdate)>21
and
to_date(SCHEDULEDATE , 'yyyy/mm/dd') between
to_date((to_char(sysdate, 'YYYY')||'/'||cast((extract(month from sysdate))as char)||'/22'),'yyyy/mm/dd') and to_date((to_char(sysdate, 'YYYY')||'/'||cast((extract(month from sysdate)+1)as char)||'/21'),'yyyy/mm/dd')
)
I'm trying to figure out a simple way of returning a set of date ranges based on the day of the Month. If the Day of the month of less than or Equal to I want it to return all orders the have a schedule date between the 22 of the Month before and the 21st of the Current month. If the Day of the month is greater than 21 I would like it return all orders that have a schedule date of the current month up to the end of the month. I've tried to use a case in the where with no luck. What I have now doesn't seem to work either. Any help would be appreciated.
I think this does what you want:
WHERE (extract(day from sysdate) <= 21 and
scheduledate >= add_months(trunc(sysdate, 'MON'), -1) + 21 and
scheduledate < trunc(sysdate, 'MON') + 21
) or
(extract(day from sysdate) > 21 and
trunc(scheduledate, 'MON') = trunc(sysdate, 'MON')
)

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

Current Financial Year to sysdate

I can't seem to find a straightforward sql without delving into PL SQL for always bringing current financial year in which case 01-04-2015 to sysdate. I want this to always update automatically so when it comes next financial year in 01/04/2016 it will bring whatever is held from that date to whenever the report is being run.
If anyone can please shed some light for me. thanks
sql is:
SELECT
PROPERTY.PRO_MANAGINGCOMPANY_DESCR,
PROPERTY.PRO_SCHEME_DESCR,
PROPERTY.PRO_SCHEME,
SUM(REPAIR_CURRENT.REP_ESTIMATED_COST) as "Estimated Cost",
nvl(SUM(REPAIR_CURRENT.REP_INVOICED_COST),SUM(REPAIR_CURRENT.REP_ESTIMATED_COST)) as "Estimated Cost Invoiced",
SUM(REPAIR_CURRENT.REP_INVOICED_COST) as "Invoice Cost",
to_char(REPAIR_CURRENT.REP_RAISED_DATE,'Mon') as "Month",
to_number(to_char(to_date(REPAIR_CURRENT.REP_RAISED_DATE,'dd-mon-yy'),'mm')) as "Month No."
FROM
PROPERTY,
REPAIR_CURRENT,
SERVICE_REQUEST
WHERE
( SERVICE_REQUEST.SRQ_PRO_REFNO=PROPERTY.PRO_REFNO )
AND ( REPAIR_CURRENT.REP_SRQ_NO=SERVICE_REQUEST.SRQ_NO )
AND
(
--PROPERTY.PRO_SCHEME = ( '00054' )
--AND
REPAIR_CURRENT.REP_RAISED_DATE BETWEEN '01-APR-2015' AND sysdate
AND
REPAIR_CURRENT.REP_STATUS <> 'CAN'
)
GROUP BY
PROPERTY.PRO_MANAGINGCOMPANY_DESCR,
PROPERTY.PRO_SCHEME_DESCR,
PROPERTY.PRO_SCHEME,
to_char(REPAIR_CURRENT.REP_RAISED_DATE,'Mon'),
to_number(to_char(to_date(REPAIR_CURRENT.REP_RAISED_DATE,'dd-mon-yy'),'mm'))
If you just want to get the beginning of the fiscal year for the current date:
SELECT TO_DATE('01-04' || CASE
WHEN EXTRACT(MONTH FROM SYSDATE) > 4 THEN
EXTRACT(YEAR FROM SYSDATE)
ELSE
EXTRACT(YEAR FROM SYSDATE)-1
END, 'DD-MM-RRRR') FISCAL_YEAR
FROM DUAL
This works for any date:
REPAIR_CURRENT.REP_RAISED_DATE
BETWEEN Add_Months(Trunc(Add_Months(sysdate,-3),'YYYY'),3)
AND Sysdate
Basically, subtract three months, truncate to the year, and add three months back on.
To just get the financial year for a date, use:
Extract(Year from Add_Months(Trunc(Add_Months(sysdate,-3),'YYYY'),3))
SELECT *
FROM your_table
WHERE datetime >= CASE
WHEN SYSDATE < TRUNC( SYSDATE, 'YEAR' ) + INTERVAL '3' MONTH
THEN TRUNC( SYSDATE, 'YEAR' ) - INTERVAL '9' MONTH
ELSE TRUNC( SYSDATE, 'YEAR' ) + INTERVAL '3' MONTH
END;
Thank you, the following worked! add_months(trunc(sysdate,'year'),3) AND sysdate
thank you all for your input :)
REPAIR_CURRENT.REP_RAISED_DATE BETWEEN '01-APR-2015' AND sysdate
Firstly, '01-APR-2015' is not a DATE it is a string. You must always use TO_DATE along with proper format model to explicitly convert the string into DATE. Or, use the ANSI Date literal as you are not concerned with the time portion. It uses a fixed format 'YYYY-MM-DD'.
Now, coming to your date arithmetic, you could use a CASE expression to evaluate the financial date depending on the year.
REP_RAISED_DATE
BETWEEN
CASE
WHEN
SYSDATE < ADD_MONTHS(TRUNC(SYSDATE, 'YEAR'), 3)
THEN
ADD_MONTHS(TRUNC(SYSDATE, 'YEAR') , -9)
ELSE
ADD_MONTHS(TRUNC(SYSDATE, 'YEAR'), 3)
END
AND SYSDATE
Basically, SYSDATE >= ADD_MONTHS(TRUNC(SYSDATE, 'YEAR'), 3) is to check whether SYSDATE is greater than 1-APR of current year. And, SYSDATE < ADD_MONTHS(TRUNC(SYSDATE, 'YEAR'), 15) is to check whether it is between JAN and MARCH of next year.
For example,
SQL> SELECT
2 CASE
3 WHEN
4 SYSDATE < ADD_MONTHS(TRUNC(SYSDATE, 'YEAR'), 3)
5 THEN
6 ADD_MONTHS(TRUNC(SYSDATE, 'YEAR') ,-9)
7 ELSE
8 ADD_MONTHS(TRUNC(SYSDATE, 'YEAR'), 3)
9 END FINANCIAL_YEAR
10 FROM dual;
FINANCIAL
---------
01-APR-15
For date between JAN and MAR of next year:
SQL> SELECT
2 CASE
3 WHEN
4 DATE '2016-02-01' < ADD_MONTHS(TRUNC(DATE '2016-02-01', 'YEAR'), 3)
5 THEN
6 ADD_MONTHS(TRUNC(DATE '2016-02-01', 'YEAR') ,-9)
7 ELSE
8 ADD_MONTHS(TRUNC(DATE '2016-02-01', 'YEAR'), 3)
9 END FINANCIAL_YEAR
10 FROM dual;
FINANCIAL
---------
01-APR-15
Following SQLreturns start and end date for Financial Year of current date.
SELECT
TO_DATE('01-04' || EXTRACT(YEAR FROM add_months(sysdate, -3)),'DD-MM-RRRR') from_dt ,
TO_DATE('31-03' || EXTRACT(YEAR FROM add_months(sysdate, 9)),'DD-MM-RRRR') to_dt from dual;
For any random date, you can use the following SQL: example for 01-04-2020
SELECT
TO_DATE('01-04' || EXTRACT(YEAR FROM add_months(to_date('01-04-2020','DD-MM-RRRR'), -3)),'DD-MM-RRRR') from_dt ,
TO_DATE('31-03' || EXTRACT(YEAR FROM add_months(to_date('01-04-2020','DD-MM-RRRR'), 9)),'DD-MM-RRRR') to_dt from dual;