oracle month to day - sql

I have data as below in which data is splitted as below where month 1 represent JAN ,Month 2 represent FEB and so on.
MONTH VALUE
1 93
2 56
3 186
4 60
Now I need to calculate sum of value based on number of days.
Ex- If date is between Jan 1st to March 31st then SUM is 335(93+56+186)
If date is between Jan 17th to March 31st then sum is 287(45+56+186) .
Here 45 for month of january is dervied from average per day for month of JAN (93/31==3) .Multiplied by number of days in January for the required period.
Ignore leap year and FEB month can be taken 28 days always.

As one of the approaches, you can turn a month into a list of days(dates) that constitute it (ease filtering operation), and perform calculation as follows:
/* sample of data that you've provided */
with t1(mnth,val) as(
select 1, 93 from dual union all
select 2, 56 from dual union all
select 3, 186 from dual union all
select 4, 60 from dual
),
/*
Generates current year dates
From January 1st 2014 to December 31st 2014
*/
dates(dt) as(
select trunc(sysdate, 'YEAR') - 1 + level
from dual
connect by extract(year from (trunc(sysdate, 'YEAR') - 1 + level)) <=
extract(year from sysdate)
)
/*
The query that performs calculations based on range of dates
*/
select sum(val / extract(day from last_day(dt))) as result
from dates d
join t1
on (extract(month from d.dt) = t1.mnth)
where dt between date '2014-01-17' and -- January 17th 2014 to
date '2014-03-31' -- March 31st 2014
Result:
RESULT
----------
287

Related

Get the last 4 weeks prior to current week

I have a table that has DATE_VALUE, FISCAL_WEEK, FISCAL_YEAR
DATE_VALUE FISCAL_WEEK FISCAL_YEAR
24-DEC-21 52 2021
25-DEC-21 52 2021
26-DEC-21 52 2021
27-DEC-21 53 2021
28-DEC-21 53 2021
29-DEC-21 53 2021
30-DEC-21 53 2021
31-DEC-21 53 2021
01-JAN-22 53 2021
02-JAN-22 53 2021
03-JAN-22 1 2022
04-JAN-22 1 2022
05-JAN-22 1 2022
06-JAN-22 1 2022
07-JAN-22 1 2022
08-JAN-22 1 2022
09-JAN-22 1 2022
10-JAN-22 2 2022
11-JAN-22 2 2022
12-JAN-22 2 2022
13-JAN-22 2 2022
14-JAN-22 2 2022
The table goes on for the entire FY 2021 & 2022
I want to get the last 4 fiscal weeks (FW) prior to the current week. Let's assume this week is FW20 FY2022, I am able to get this result:
FISCAL_WEEK FISCAL_YEAR
16 2022
17 2022
18 2022
19 2022
The code used to return the above output is:
SELECT
*
FROM
(
WITH t AS (
SELECT
fiscal_week - 1 lastweek,
fiscal_week - 5 week_x,
fiscal_year
FROM
TABLE
WHERE
Trunc(date_value) = Trunc(sysdate)
)
SELECT
DISTINCT fiscal_week,
t.fiscal_year
FROM
TABLE
OUTER JOIN t ON fiscal_week <> week_x
WHERE
to_char(fiscal_week) BETWEEN lastweek - 4
AND lastweek
ORDER BY
fiscal_week
)
But if the current week was FW04 FY2022, the code above is not able to return this desired output.
FISCAL_WEEK FISCAL_YEAR
53 2021
1 2022
2 2022
3 2022
Similarly, if the current was FY03 FY2022, I want the output to be:
FISCAL_WEEK FISCAL_YEAR
52 2021
53 2021
1 2022
2 2022
How do I need to write the code to get this output? Maybe the case statement could work but I'd like to see if there's any other workaround? Any help would be appreciated.
Thank you!
You may put the condition on the date value not the week to get the required output, then use OFFSET 1 to skip the current week and fetch the next 4 rows only. Try the following:
WITH T AS
(
SELECT DISTINCT fiscal_week, fiscal_year
FROM TABLE_NAME
WHERE Trunc(date_value) <= Trunc(SYSDATE)
ORDER BY fiscal_year DESC, fiscal_week DESC
OFFSET 1 ROWS
FETCH NEXT 4 ROWS ONLY
)
SELECT fiscal_week, fiscal_year
FROM T ORDER BY fiscal_year, fiscal_week
See a demo.
Don't use the FISCAL_WEEK and FISCAL_YEAR columns; just use the DATE column and compare it to a range based on the start of the ISO week:
SELECT DISTINCT fiscal_week, fiscal_year
FROM table_name
WHERE "DATE" < TRUNC(SYSDATE, 'IW')
AND "DATE" >= TRUNC(SYSDATE, 'IW') - INTERVAL '28' DAY
ORDER BY fiscal_year, fiscal_week;
Which, for the sample data:
Create Table table_name("DATE", FISCAL_WEEK, FISCAL_YEAR) AS
SELECT DATE '2021-12-24', 52, 2021 FROM DUAL UNION ALL
SELECT DATE '2021-12-25', 52, 2021 FROM DUAL UNION ALL
SELECT DATE '2021-12-26', 52, 2021 FROM DUAL UNION ALL
SELECT DATE '2021-12-27', 53, 2021 FROM DUAL UNION ALL
SELECT DATE '2021-12-28', 53, 2021 FROM DUAL UNION ALL
SELECT DATE '2021-12-29', 53, 2021 FROM DUAL UNION ALL
SELECT DATE '2021-12-30', 53, 2021 FROM DUAL UNION ALL
SELECT DATE '2021-12-31', 53, 2021 FROM DUAL UNION ALL
SELECT DATE '2022-01-01', 53, 2021 FROM DUAL UNION ALL
SELECT DATE '2022-01-02', 53, 2021 FROM DUAL UNION ALL
SELECT DATE '2022-01-03', 1, 2022 FROM DUAL UNION ALL
SELECT DATE '2022-01-04', 1, 2022 FROM DUAL UNION ALL
SELECT DATE '2022-01-05', 1, 2022 FROM DUAL UNION ALL
SELECT DATE '2022-01-06', 1, 2022 FROM DUAL UNION ALL
SELECT DATE '2022-01-07', 1, 2022 FROM DUAL UNION ALL
SELECT DATE '2022-01-08', 1, 2022 FROM DUAL UNION ALL
SELECT DATE '2022-01-09', 1, 2022 FROM DUAL UNION ALL
SELECT DATE '2022-01-10', 2, 2022 FROM DUAL UNION ALL
SELECT DATE '2022-01-11', 2, 2022 FROM DUAL UNION ALL
SELECT DATE '2022-01-12', 2, 2022 FROM DUAL UNION ALL
SELECT DATE '2022-01-13', 2, 2022 FROM DUAL UNION ALL
SELECT DATE '2022-01-14', 2, 2022 FROM DUAL UNION ALL
SELECT DATE '2022-02-14', 6, 2022 FROM DUAL;
If SYSDATE was 2022-01-17, would output:
FISCAL_WEEK
FISCAL_YEAR
52
2021
53
2021
1
2022
2
2022
fiddle

Combining year and month into a same column

How can I combine year and month into a one column with format YYYYMM? My data doesn't have leading zeros. Below are example columns.
Year
Month
2019
7
2019
10
2020
11
2020
3
2021
1
In most RDBMS platforms, you can CONCAT both values, while using a bit of string manipulation trickery on the Month field to get it to conform to the format by adding a leading 0 in the event of a single-digit month value:
SELECT CONCAT(Year, RIGHT(CONCAT('0', Month), 2) AS YearMonth FROM Dates
YearMonth
201907
201910
202011
202003
202101
db<>fiddle.uk
You can combine them as a number:
year * 100 + month
If you want a string, you can convert this to a string:
cast(year * 100 + month as char(6))
The way you put it, concatenation along with the LPAD function might be what you're looking for. See line #8.
SQL> with test (year, month) as
2 (select 2019, 7 from dual union all
3 select 2019, 10 from dual union all
4 select 2020, 11 from dual union all
5 select 2020, 3 from dual
6 )
7 select year, month,
8 year || lpad(month, 2, '0') result
9 from test;
YEAR MONTH RESULT
---------- ---------- ----------------------------------
2019 7 201907
2019 10 201910
2020 11 202011
2020 3 202003
SQL>

Need SQL to generate multiple rows from subquery

I have a query that selects a month and year from a complex heirachy of tables. To simplify this question, we can use this query:
select 2 as month, 2018 as year from dual;
I need SQL that will use that query as a subquery to output 3 row: that month as well as with the preceeding 2 months (including years). So the output needed for that specific case would be:
Month Year
2 2018
1 2018
12 2017
I have no idea how to proceed. Ideas anyone?
If you have the month/year already in a DATE column, it would be one less conversion, but if they are separate columns like you have provided, you can use a query like the one below.
Query
WITH sample_data AS (SELECT 2 AS month, 2018 AS year FROM DUAL)
SELECT TO_CHAR (ADD_MONTHS (TO_DATE (month || '-' || year, 'MM-YYYY'), ((LEVEL * -1) + 1)), 'MM')
AS month,
TO_CHAR (ADD_MONTHS (TO_DATE (month || '-' || year, 'MM-YYYY'), ((LEVEL * -1) + 1)), 'YYYY')
AS year
FROM sample_data
CONNECT BY LEVEL <= 3;
Result
MONTH YEAR
________ _______
02 2018
01 2018
12 2017
For Oracle 12+:
select *
from
(select 2 as month, 2018 as year
from dual) complex_query
,lateral(
select
extract(month from add_months(dt, 1-level)) as month,
extract(year from add_months(dt, 1-level)) as year
from (
select
to_date(complex_query.year||'-'||complex_query.month||'-01', 'yyyy-mm-dd') as dt
from dual
)
connect by level<=3
);
For previous versions:
select
complex_query.*
,extract(year from (to_date(year||'-'||month,'yyyy-mm')-delta)) year_2
,extract(month from (to_date(year||'-'||month,'yyyy-mm')-delta)) month_2
from
(select 2 as month, 2018 as year
from dual) complex_query
,(select
NUMTOYMINTERVAL(level-1,'month') delta
from dual
connect by level<=3
) v
Results:
MONTH YEAR YEAR_2 MONTH_2
---------- ---------- ---------- ----------
2 2018 2018 1
2 2018 2017 12
2 2018 2017 11
Would union work?
with primary_month as (
select 2 as month, 2018 as year from dual
)
select (case when month = 1 then 13 else month end) - 1 as month , case when month = 1 then year-1 else year end as year from primary_month
union
select month, year from primary_month
union
select month + 1 as month, year from primary_month
;
This example won't handle boundary cases like if the primary month is January, since there is no month 0, but I'm not sure if that's what you're going for and it depends on the real query and schema (e.g. are you using timestamp columns?)

ORA-01848: day of year must be between 1 and 365 (366 for leap year) error

I have existing data with day of the year (1-366) stored in DDD format.
Now, when I am trying to query the data and pull out report in MM/DD/YYYY format, I am getting
ORA-01848: day of year must be between 1 and 365 (366 for leap year) for the below query
select to_CHAR(TO_DATE(MyColumn, 'DDD'),'MM/DD/YYYY') from MyTable;
How to retrieve date in MM/DD/YYYY format when the year is leap year?
Your current query:
select to_CHAR(TO_DATE(MyColumn, 'DDD'),'MM/DD/YYYY') from MyTable;
is defaulting to the current year. All of the converted values will show 2018:
-- CTE for dummy values (<= 365)
with mytable(mycolumn) as (
select 1 from dual
union all select 60 from dual
union all select 365 from dual
)
select to_CHAR(TO_DATE(MyColumn, 'DDD'),'MM/DD/YYYY') from MyTable;
TO_CHAR(TO
----------
01/01/2018
03/01/2018
12/31/2018
As 2018 isn't a leap year, day 366 isn't valid. You could make it use an arbitrary hard-coded leap year:
select to_CHAR(TO_DATE('2000' || MyColumn, 'YYYYDDD'),'MM/DD/YYYY') from MyTable;
Demo:
-- CTE for dummy values
with mytable(mycolumn) as (
select 1 from dual
union all select 60 from dual
union all select 365 from dual
union all select 366 from dual
)
select to_CHAR(TO_DATE('2000' || MyColumn, 'YYYYDDD'),'MM/DD/YYYY') from MyTable;
TO_CHAR(TO
----------
01/01/2000
02/29/2000
12/30/2000
12/31/2000
But if the original date wasn't from a leap year then the values will be out by a day - as well as being shown against the wrong year, of course.
You could filter out values with values > 365 and stick to the current year, but again you're likely to get unrealistic/unhelpful converted dates. Or you could use 12c's default ... on conversion error syntax to get say a null result when it won't convert, but again other dates will be inconsistent.
Unless you know the year each DDD values represents you can't get an accurate conversion.
If you have another column that holds the year then concatenate them together, e.g. if that year column is called MyYear:
select to_CHAR(TO_DATE(MyYear || MyColumn, 'YYYYDDD'),'MM/DD/YYYY') from MyTable;
Demo showing varying resulsts:
-- CTE for dummy values
with mytable(mycolumn, myyear) as (
select 1, 2018 from dual
union all select 60, 2016 from dual
union all select 60, 2017 from dual
union all select 365, 2016 from dual
union all select 366, 2016 from dual
union all select 365, 2017 from dual
)
select MyColumn, MyYear,
to_CHAR(TO_DATE(MyColumn default null on conversion error, 'DDD'),'MM/DD/YYYY') as Y2018,
to_CHAR(TO_DATE('2000' || MyColumn, 'YYYYDDD'),'MM/DD/YYYY') as Y2000,
to_CHAR(TO_DATE(MyYear || MyColumn, 'YYYYDDD'),'MM/DD/YYYY') as OK
from MyTable;
MYCOLUMN MYYEAR Y2018 Y2000 OK
---------- ---------- ---------- ---------- ----------
1 2018 01/01/2018 01/01/2000 01/01/2018
60 2016 03/01/2018 02/29/2000 02/29/2016
60 2017 03/01/2018 02/29/2000 03/01/2017
365 2016 12/31/2018 12/30/2000 12/30/2016
366 2016 12/31/2000 12/31/2016
365 2017 12/31/2018 12/30/2000 12/31/2017
I created a logic which would calculate the date as per the input day. When its not a leap year, it would display the date. Incase of leap year it would display the last day if day number is 366. If it's not a leap year, it would display the last day of the year if day number is 366, hence it would never result in the error you faced.
with tb(col1) as (Select level
from dual
connect by level < 367)
-- Actual Query ..Col1 is the day number being passed.
Select Case when col1 < 366 then
to_char(TO_DATE(col1, 'DDD'),'MM/DD/YYYY')
else
to_char(LAST_DAY(ADD_MONTHS(TRUNC(SYSDATE , 'Year'),11)),'MM/DD/YYYY')
end col
from tb ;
DEMO

Calculating holidays: number of saturdays and sundays within the given date range query in Oracle

I want to calculate holidays: the number of Saturdays and Sundays within the given date range query in Oracle.
You could use the ROW GENERATOR technique to first generate the dates for a given range, and then count only the SATURDAYs and SUNDAYs.
For example, this query will give me the total count of saturdays and sundays between 1st Jan 2014 and 31st Dec 2014 -
SQL> WITH DATA AS
2 (SELECT to_date('01/01/2014', 'DD/MM/YYYY') date1,
3 to_date('31/12/2014', 'DD/MM/YYYY') date2
4 FROM dual
5 )
6 SELECT SUM(holiday) holiday_count
7 FROM
8 (SELECT
9 CASE
10 WHEN TO_CHAR(date1+LEVEL-1, 'DY','NLS_DATE_LANGUAGE=AMERICAN') IN ('SAT', 'SUN')
11 THEN 1
12 ELSE 0
13 END holiday
14 FROM data
15 CONNECT BY LEVEL <= date2-date1+1
16 )
17 /
HOLIDAY_COUNT
-------------
104
SQL>