Calculate manual week number of year in Oracle SQL - sql

i have a date column and along that i need to calculate another column in oracle for week number of they year, the weeks should be from sunday to saturday, starting first day of the year.
for example for current year
Week 1 : 1 Jan 2020 (Wednesday) - 4 Jan 2020(Saturday)
Week 2 : 5 Jan 2020 (Sunday) - 11 Jan 2020(Saturday)
. . . . .
Week 5 : 26 Jan 2020 (Sunday) - 1 Feb 2020 (Saturday)
and so on...

You need to write your own logic using a hierarchy query.
Something like the following:
SQL> SELECT WEEKNUMBER,
2 WEEK_START,
3 CASE WHEN WEEKNUMBER = 1 THEN FIRST_WEEKEND ELSE WEEK_START + 6 END AS WEEK_END
4 FROM
5 (SELECT
6 CASE
7 WHEN LEVEL = 1 THEN FIRST_DAY
8 ELSE FIRST_WEEKEND + ( LEVEL - 2 ) * 7 + 1
9 END AS WEEK_START,
10 FIRST_WEEKEND,
11 LEVEL AS WEEKNUMBER
12 FROM
13 ( SELECT
14 TRUNC(SYSDATE, 'YEAR') FIRST_DAY,
15 NEXT_DAY(TRUNC(SYSDATE, 'YEAR'), 'SATURDAY') FIRST_WEEKEND
16 FROM DUAL )
17 CONNECT BY
18 CASE WHEN LEVEL = 1 THEN FIRST_DAY
19 ELSE FIRST_WEEKEND + ( LEVEL - 2 ) * 7 + 1
20 END < ADD_MONTHS(TRUNC(SYSDATE, 'YEAR'), 12));
WEEKNUMBER WEEK_STAR WEEK_END
---------- --------- ---------
1 01-JAN-20 04-JAN-20
2 05-JAN-20 11-JAN-20
3 12-JAN-20 18-JAN-20
4 19-JAN-20 25-JAN-20
5 26-JAN-20 01-FEB-20
6 02-FEB-20 08-FEB-20
7 09-FEB-20 15-FEB-20
8 16-FEB-20 22-FEB-20
9 23-FEB-20 29-FEB-20
10 01-MAR-20 07-MAR-20
11 08-MAR-20 14-MAR-20
.......
.......
53 27-DEC-20 02-JAN-21
Cheers!!

One other option would be
with t as
(
select trunc(sysdate,'yyyy')+level-1 as day, to_char(trunc(sysdate,'yyyy')+level-1,'DY','NLS_DATE_LANGUAGE=AMERICAN') as weekday, level - 1 as lvl
from dual
connect by level <= 366
), t1 as
(
select case
when lvl = 0 then
day
else
case
when weekday = 'SUN' then
day
end
end as day1,
lvl
from t
), t2 as
(
select case
when weekday = 'SAT' then
day
end as day2,
lvl
from t
)
select concat( 'Week ', wk1) as week, day1, day2
from
(
select row_number() over (order by lvl) wk1, day1
from t1 where day1 is not null ) t1
left join
(
select row_number() over (order by lvl) wk2, day2
from t2 where day2 is not null ) t2
on wk2 = wk1
by using select .. from dual connect by level syntax and case..when expression to scan all the current year.
Demo

Related

split from date and To date in multiple months using oracle sql

In need to split lines with From date and To date in multiple months.
I want to split like this.Target
Sample 1
10/02/2023 - 28/02/2023
Target
10/02/2023 - 28/02/2023
Sample 2
10/02/2023 - 29/08/2023
Target
10/02/2023 - 28/02/2023
01/03/2023 - 31/03/2023
01/04/2023 - 29/08/2023
Sample 3
01/04/2022 - 31/03/2023
Target
01/04/2022 - 28/02/2023
01/03/2023 - 31/03/2023
I succeed in first steps but I'm now stucked
For the moment I can only do like this[Existing]
But in yellow wrong values,
Here below my code
CASE WHEN qd.valid_from >= TRUNC(add_months(qd.valid_from,COLUMN_VALUE - 1),'MM')
THEN
TRUNC(qd.valid_from)
ELSE
TRUNC(add_months(qd.valid_from,COLUMN_VALUE - 1),'MM')
END new_start_date,
CASE WHEN last_day(TRUNC(add_months(qd.valid_from,COLUMN_VALUE - 1),'MM')) >= last_day(TRUNC(add_months(qd.valid_from,2),'MM'))
THEN
TRUNC(qd.valid_to)
ELSE
TRUNC(last_day(TRUNC(add_months(qd.valid_from,COLUMN_VALUE - 1),'MM')))
END new_end_date
FROM QUOTATIONS_UO QH
),
TABLE(
CAST(
MULTISET
(
SELECT LEVEL
FROM dual
CONNECT BY add_months(TRUNC(qd.valid_from,'MM'),LEVEL - 1) <= add_months(TRUNC(qd.valid_from,'MM'),2)
) AS sys.OdciNumberList
)
)
)
It is unclear what is it about months february and march vs all the others, but if you want to split dates by months then maybe this could help you:
Select DISTINCT
ID,
LEVEL "LVL",
CASE WHEN LEVEL = 1 THEN FROM_DATE END "FROM_DATE",
CASE WHEN LEVEL = 1 THEN UNTIL_DATE END "UNTIL_DATE",
CASE WHEN MONTH_UNTIL = MONTH_FROM THEN FROM_DATE
WHEN LEVEL = 1 THEN FROM_DATE
WHEN MONTHS > 1 And LEVEL < MONTHS THEN LAST_DAY(ADD_MONTHS(FROM_DATE, LEVEL - 2)) + 1
WHEN MONTHS > 1 And LEVEL = MONTHS THEN LAST_DAY(ADD_MONTHS(FROM_DATE, LEVEL - 2)) + 1
WHEN MONTHS > 0 And MONTHS < LEVEL THEN LAST_DAY(ADD_MONTHS(FROM_DATE, LEVEL - 2)) + 1
ELSE UNTIL_DATE
END "FROM_DATE_2",
--
CASE WHEN MONTH_UNTIL = MONTH_FROM THEN UNTIL_DATE
--WHEN LEVEL = 1 THEN LAST_DAY(ADD_MONTHS(FROM_DATE, LEVEL - 1))
WHEN MONTHS > 1 And LEVEL < MONTHS THEN LAST_DAY(ADD_MONTHS(FROM_DATE, LEVEL - 1))
WHEN MONTHS > 1 And LEVEL = MONTHS THEN LAST_DAY(ADD_MONTHS(FROM_DATE, LEVEL - 1))
ELSE UNTIL_DATE
END "UNTIL_DATE_2"
FROM
(
Select
ID, FROM_DATE, UNTIL_DATE,
To_Char(FROM_DATE, 'yyyymm') "MONTH_FROM",
To_Char(UNTIL_DATE, 'yyyymm') "MONTH_UNTIL",
FLOOR(MONTHS_BETWEEN(UNTIL_DATE, FROM_DATE)) "MONTHS"
From
tbl
)
CONNECT BY LEVEL < MONTHS + 2
ORDER BY ID, LVL
... which with sample data like here:
WITH
tbl (ID, FROM_DATE, UNTIL_DATE) AS
(
Select 1, To_Date('10.02.2023', 'dd.mm.yyyy'), To_Date('28.02.2023', 'dd.mm.yyyy') From Dual Union All
Select 2, To_Date('10.02.2023', 'dd.mm.yyyy'), To_Date('29.08.2023', 'dd.mm.yyyy') From Dual Union All
Select 3, To_Date('01.04.2022', 'dd.mm.yyyy'), To_Date('31.03.2023', 'dd.mm.yyyy') From Dual
)
... results as:
ID LVL FROM_DATE UNTIL_DATE FROM_DATE_2 UNTIL_DATE_2
---------- ---------- --------- ---------- ----------- ------------
1 1 10-FEB-23 28-FEB-23 10-FEB-23 28-FEB-23
2 1 10-FEB-23 29-AUG-23 10-FEB-23 28-FEB-23
2 2 01-MAR-23 31-MAR-23
2 3 01-APR-23 30-APR-23
2 4 01-MAY-23 31-MAY-23
2 5 01-JUN-23 30-JUN-23
2 6 01-JUL-23 31-JUL-23
2 7 01-AUG-23 29-AUG-23
3 1 01-APR-22 31-MAR-23 01-APR-22 30-APR-22
3 2 01-MAY-22 31-MAY-22
3 3 01-JUN-22 30-JUN-22
3 4 01-JUL-22 31-JUL-22
3 5 01-AUG-22 31-AUG-22
3 6 01-SEP-22 30-SEP-22
3 7 01-OCT-22 31-OCT-22
3 8 01-NOV-22 30-NOV-22
3 9 01-DEC-22 31-DEC-22
3 10 01-JAN-23 31-JAN-23
3 11 01-FEB-23 28-FEB-23
3 12 01-MAR-23 31-MAR-23

How to use lead lag function in oracle

I have written some query to get my resultant result as below :
Note: I have months starting from jan-2016 to jan-2018.
There are two types, either 'hist' or 'future'
Resultant dataset :
In this example : let consider combination of id1+id2+id3 as 1,2,3
type month id1 id2 id3 value
hist jan-17 1 2 3 10
hist feb-17 1 2 3 20
future jan-17 1 2 3 15
future feb-17 1 2 3 1
hist mar-17 1 2 3 2
future apr-17 1 2 3 5
My calculation logic depends on the quarter number of month .
For eg . for month of january(first month of quarter) i want the value to be : future of jan + future value of feb + future value of march .
so for jan-17 , output should be : 15+1 + 0(for march there is no corresponding future value)
for the month of feb (2nd month of quarter), value should be : hist of jan + future of feb + future of march i.e 10+1+0(future of march is not available)
Similarly for the month of march , value should be : history of jan + history of feb + future of march i.e 10+20+0(frecast of march no present) .
similarly for april,may.june(depending on quarter number of month)
I am aware of the lead lag function , but I am not able to apply it here
Can someone please help
I would not mess with lag, this can all be done with a group by if you convert your dates to quarters:
WITH
dset
AS
(SELECT DATE '2017-01-17' month, 5 VALUE
FROM DUAL
UNION ALL
SELECT DATE '2017-02-17' month, 6 VALUE
FROM DUAL
UNION ALL
SELECT DATE '2017-03-25' month, 7 VALUE
FROM DUAL
UNION ALL
SELECT DATE '2017-05-25' month, 4 VALUE
FROM DUAL)
SELECT SUM (VALUE) value_sum, TO_CHAR (month, 'q') quarter, TO_CHAR (month, 'YYYY') year
FROM dset
GROUP BY TO_CHAR (month, 'q'), TO_CHAR (month, 'YYYY');
This results in:
VALUE_SUM QUARTER YEAR
18 1 2017
4 2 2017
We can use an analytic function if you need the result on each record:
SELECT SUM (VALUE) OVER (PARTITION BY TO_CHAR (month, 'q'), TO_CHAR (month, 'YYYY')) quarter_sum, month, VALUE
FROM dset
This results in:
QUARTER_SUM MONTH VALUE
18 1/17/2017 5
18 2/17/2017 6
18 3/25/2017 7
4 5/25/2017 4
Make certain you include year, you don't want to combine quarters from different years.
Well, as said in one of the comments.. the trick lies in another question of yours & the corresponding answer. Well... it goes somewhat like this..
with
x as
(select 'hist' type, To_Date('JAN-2017','MON-YYYY') ym , 10 value from dual union all
select 'future' type, To_Date('JAN-2017','MON-YYYY'), 15 value from dual union all
select 'future' type, To_Date('FEB-2017','MON-YYYY'), 1 value from dual),
y as
(select * from x Pivot(Sum(Value) For Type in ('hist' as h,'future' as f))),
/* Pivot for easy lag,lead query instead of working with rows..*/
z as
(
select ym,sum(h) H,sum(f) F from (
Select y.ym,y.H,y.F from y
union all
select add_months(to_Date('01-JAN-2017','DD-MON-YYYY'),rownum-1) ym, 0 H, 0 F
from dual connect by rownum <=3 /* depends on how many months you are querying...
so this dual adds the corresponding missing 0 records...*/
) group by ym
)
select
ym,
Case
When MOD(Extract(Month from YM),3) = 1
Then F + Lead(F,1) Over(Order by ym) + Lead(F,2) Over(Order by ym)
When MOD(Extract(Month from YM),3) = 2
Then Lag(H,1) Over(Order by ym) + F + Lead(F,1) Over(Order by ym)
When MOD(Extract(Month from YM),3) = 3
Then Lag(H,2) Over(Order by ym) + Lag(H,1) Over(Order by ym) + F
End Required_Value
from z

Select Where Date Between

I would like to SELECT a table calendar and combine the results with the days of the month.
I mean,
Table: Calendar
ID TEAM EMPLOYER START END
17 19 8 04/08/2014 18:01:00 11/08/2014 07:59:00
18 19 39 11/08/2014 18:01:00 18/08/2014 07:59:00
19 19 44 18/08/2014 18:01:00 25/08/2014 07:59:00
20 19 38 25/08/2014 18:01:00 01/09/2014 07:59:00
And I have a SELECT for the days of the month.
Select Days.Dt
From (Select Trunc(To_Date('2014', 'YYYY'), 'y') - 1 + Rownum Dt
From All_Objects
Where Rownum <= Add_Months(Trunc(To_Date('2014', 'YYYY'), 'y'), 12) -
Trunc(To_Date('2014', 'YYYY'), 'y')) Days
Where To_Char(Dt, 'mm/yyyy') = '08/2014'
What I want is something like this:
DAY EMPLOYER_END EMPLOYER_START
1 01/08/2014
2 02/08/2014
3 03/08/2014
4 04/08/2014 4
5 05/08/2014 4 4
6 06/08/2014 4 4
7 07/08/2014 4 4
8 08/08/2014 4 4
9 09/08/2014 4 4
10 10/08/2014 4 4
11 11/08/2014 4 39
12 12/08/2014 39 39
The employer starts at 18:01 (always) and end at 07:59 (always).
Does anyone know if it's possible?
And the way I can do that.
Thanks!
Your desired results do not match your sample data. However, I think you want something like this:
with dates as (
Select Days.Dt
From (Select Trunc(To_Date('2014', 'YYYY'), 'y') - 1 + Rownum Dt
From All_Objects
Where Rownum <= Add_Months(Trunc(To_Date('2014', 'YYYY'), 'y'), 12) -
Trunc(To_Date('2014', 'YYYY'), 'y')
) Days
Where To_Char(Dt, 'mm/yyyy') = '08/2014'
)
select d.dt,
sum(case when c.employer_start = d.ddt then 0 else 1 end) as employer_end,
sum(case when c.employer_end = d.dt then 1 else 0 end) as employer_start
from dates d left outer join
calendar c
on d.dt between c.employer_start and c.employer_end
group by d.dt
order by d.dt;
I guess this can be useful to you
WITH mindates AS
(SELECT TRUNC(MIN(startdate),'month') st_date,
TRUNC(MAX(enddate)) ed_date
FROM calendar
) ,
dates AS
(SELECT st_date+ rownum-1 AS dates_col
FROM mindates,
dual
CONNECT BY rownum <= (ed_date- st_date)+1
)
SELECT d.dates_col dates,
MIN((
CASE
WHEN d.dates_col=c.startdate
THEN NULL
ELSE c.employer
END)) AS employer_end,
MIN((
CASE
WHEN d.dates_col=c.enddate
THEN NULL
ELSE c.employer
END )) AS employer_start
FROM dates d
LEFT OUTER JOIN calendar c
ON d.dates_col BETWEEN c.startdate AND c.enddate
GROUP BY d.dates_col
ORDER BY d.dates_col;

Oracle sql sort week days by current day

I am trying to sort the days based on the order: Saturday, Sunday, Monday, Tuesday, Wednesday, Thursday, Friday.
I am trying using case:
select day,
CASE day
WHEN 1 THEN 1
WHEN 2 THEN 2
WHEN 3 THEN 3
WHEN 4 THEN 4
WHEN 5 THEN 5
WHEN 6 THEN 6
WHEN 7 THEN 7
else 0
END as day_nr
from week where day in (1,2,3,4,5,6,7)
order by day_nr asc
This is ok when I select all the days of the week. But if I want only for the day 1,5,6 the ordering is not correct. Gets the first day -Monday. How to proceed?
If you're trying to sort a set of dates by day of the week, with Saturday being the first, then consider ordering by a modified date:
create table t1(my_date date);
insert into t1
select trunc(sysdate)+rownum
from dual
connect by level <= 20
select
my_date,
to_char(my_date,'Day'),
to_char(my_date,'D')
from
t1
order by
to_char(my_date + 1,'D');
http://sqlfiddle.com/#!4/5940b/3
The downside is that it's not very intuitive, so add a code comment if you use this method.
Edit: Where you have a list of numbers, order by a case statement with either a list conversion:
case day
when 1 then 3
when 2 then 4
when 3 then 5
when 4 then 6
when 5 then 7
when 6 then 1 -- saturday
when 7 then 2
end
... or the more compact, but not as intuitive:
case
when day <= 5 then day + 2
else day - 5
end
order by case
In Oracle day 1 is Sunday by default.
SELECT * FROM
(
SELECT trunc(sysdate) + LEVEL-1 my_dt
, TO_CHAR(trunc(sysdate) + LEVEL-1, 'DY') Wk_Day
, TO_CHAR(trunc(sysdate) + LEVEL-2, 'D' ) Day#
FROM dual
CONNECT BY LEVEL <= 10
)
WHERE Day# IN (1,5,6)
ORDER BY my_dt, Day#
/
MY_DT WK_DAY DAY#
------------------------
5/10/2013 FRI 5
5/11/2013 SAT 6
5/13/2013 MON 1
5/17/2013 FRI 5
5/18/2013 SAT 6

Oracle : break down multiple entries monthwise

i have data in table as below
start_date end_date amount
15-01-2012 25-01-2012 100
26-01-2012 10-02-2012 100
11-02-2012 29-02-2012 100
i want these entries to be break down monthwise as below
15-01 to 25-01 = 100
26-01 to 31-01 = 100/((10-02-2012 - 26-01-2012) +1 ) * ((31-01-2012 - 26-01-2012) + 1) = 37.5
so for period of 15-01-2012 to 31-01-2012 = 100 + 37.5 = 137.5
for month of february amount should be
01-02 to 10-02 = 100/((10-02-2012 - 26-01-2012) +1 ) * ((01-02-2012 - 10-02-2012) + 1) = 62.5
11-02 to 29-02 = 100
so for the february 01-02-2012 to 29-02-2012 = 62.5 + 100 = 162.5
so that final output should be
start_date end_date amount
15-01-2012 31-01-2012 137.5
01-02-2012 29-02-2012 162.5
is there any method to achieve this without using PLSQL
you can use a LAG function to determine the daily average between one row and a previous (sorted) row.
once you have a daily average, you can multiply by the number of days in the period.
all in the same sql statement.
I'm not sure how you want to calculate the values, but as a start, try to break the records monthly:
with dts as (select last_day(add_months(
to_date('20111201','yyyymmdd'),level)) ld,
add_months(
to_date('20111201','yyyymmdd'),level) sd
from dual connect by level < 12)
select case when start_date >= sd then start_date else sd end st_dt,
case when end_date <= ld then end_date else ld end en_dt, amount,
ld-sd days_in_month,
case when end_date <= ld then end_date else ld end-
case when start_date >= sd then start_date else sd end part
from t, dts
where (start_date >= sd and to_char(start_date, 'yyyymm') =
to_char(sd, 'yyyymm'))
or (end_date <= ld and to_char(end_date, 'yyyymm') =
to_char(ld, 'yyyymm'))
SQL> select * from q10315606;
START_DATE END_DATE AMOUNT
---------- ---------- ----------
2012-01-15 2012-01-25 100
2012-01-26 2012-02-10 100
2012-02-11 2012-02-29 100
SQL> with day_generator as
2 -- this block expands out every date in the entire date range
3 (
4 select min_start_date + level - 1 as date_value
5 from (select min(start_date) as min_start_date,
6 max(end_date) - min(start_date) + 1 as total_days
7 from q10315606
8 )
9 connect by level <= total_days
10 ),
11 range_averages as
12 -- this block generates a daily average for each date range
13 (
14 select start_date, end_date,
15 end_date - start_date + 1 as days_inclusive,
16 amount / (end_date - start_date + 1) as daily_amount
17 from q10315606
18 )
19 select start_date, end_date, sum(amount) as amount
20 from (
21 -- here we cast all the dates to the minimum and maximum value found in the month
22 select min(dg.date_value)
23 over (partition by last_day(dg.date_value) ) as start_date,
24 max(dg.date_value)
25 over (partition by last_day(dg.date_value) ) as end_date,
26 ra.daily_amount as amount
27 from range_averages ra,
28 day_generator dg
29 where dg.date_value between ra.start_date and ra.end_date
30 )
31 group by start_date, end_date
32* order by start_date, end_date
amusch#AGDEV:SQL> /
START_DATE END_DATE AMOUNT
---------- ---------- ----------
2012-01-15 2012-01-31 137.5
2012-02-01 2012-02-29 162.5
This is a try:
I had a join with months. If a row is on two months will be "duplicated", more precisely will by distributed on both months.
For every row*month I apply the formula amount/(days in current row) * (days in current month)
with months as
(select add_months('1-Jan-2012', level -1) as mon
from dual
connect by level < 12)
select mon, sum(amount)
from(
select
months.mon,
amount/(end_date - start_date +1)*
(case when trunc(start_date,'MM') = months.mon
then least(last_day(start_date), end_date) - start_date +1
else end_date - greatest(months.mon, start_date) +1
end) as amount
from your_table y
join months on
(months.mon between trunc(start_date,'MM') and trunc(end_date, 'MM'))
)
group by mon;
This code was tested and gives exactly your desired results.
I see A.B. Cade beat me to it, but here's my solution:
SQL> ALTER SESSION SET NLS_DATE_FORMAT = 'DD-MM-YYYY'
2 /
Session altered.
SQL> CREATE TABLE t (start_date DATE, end_date DATE, amount NUMBER);
Table created.
SQL> INSERT INTO t VALUES (TO_DATE('20120115','YYYYMMDD'),TO_DATE('20120125','YYYYMMDD'),100);
1 row created.
SQL> INSERT INTO t VALUES (TO_DATE('20120126','YYYYMMDD'),TO_DATE('20120210','YYYYMMDD'),100);
1 row created.
SQL> INSERT INTO t VALUES (TO_DATE('20120211','YYYYMMDD'),TO_DATE('20120229','YYYYMMDD'),100);
1 row created.
SQL>
Having set up some test data...
SQL> COL for_month FOR A9
SQL> COL pro_rated_start FOR A15
SQL> COL pro_rated_end FOR A13
SQL>
... and formatted some columns ...
SQL> WITH months AS (
2 SELECT TRUNC(MIN(start_date),'MM') min_start_month
3 , MONTHS_BETWEEN(TRUNC(MAX(end_date),'MM'),TRUNC(MIN(start_date),'MM')) + 1 mos
4 FROM t
5 )
6 , offset AS (
7 SELECT ROWNUM - 1 r
8 FROM (SELECT NULL
9 FROM DUAL
10 CONNECT BY LEVEL <= (SELECT mos FROM months))
11 )
12 , ranges AS (
13 SELECT ADD_MONTHS(months.min_start_month, offset.r) mo_start
14 , LAST_DAY(ADD_MONTHS(months.min_start_month, offset.r)) mo_end
15 FROM offset
16 , months
17 )
18 SELECT TO_CHAR(GREATEST(t.start_date,ranges.mo_start),'Mon YYYY') for_month
19 , MIN(GREATEST(t.start_date,ranges.mo_start)) pro_rated_start
20 , MAX(LEAST(t.end_date,ranges.mo_end)) pro_rated_end
21 , SUM(t.amount
22 * CASE
23 WHEN t.end_date < ranges.mo_end
24 AND t.start_date > ranges.mo_start
25 THEN 1
26 ELSE ((LEAST(t.end_date,ranges.mo_end)
27 - GREATEST(t.start_date,ranges.mo_start) + 1)
28 / (t.end_date - t.start_date + 1))
29 END) pro_rated_amount
30 FROM t
31 , ranges
32 WHERE t.start_date <= ranges.mo_end
33 AND t.end_date >= ranges.mo_start
34 GROUP BY TO_CHAR(GREATEST(t.start_date,ranges.mo_start),'Mon YYYY');
FOR_MONTH PRO_RATED_START PRO_RATED_END PRO_RATED_AMOUNT
--------- --------------- ------------- ----------------
Jan 2012 15-01-2012 31-01-2012 137.5
Feb 2012 01-02-2012 29-02-2012 162.5
SQL>