Sample Data:
date1
date2
fiscal mon
days diff
22-10-21
29-10-21
Oct21
8
30-10-21
26-11-21
Nov21
28
27-11-21
24-12-21
Dec21
28
25-12-21
28-01-22
Jan22
35
29-01-22
25-02-22
Feb22
28
26-02-22
25-03-22
Mar22
28
26-03-22
29-04-22
Apr22
35
30-04-22
27-05-22
May22
28
28-05-22
24-06-22
Jun22
28
25-06-22
29-07-22
Jul22
35
30-07-22
13-08-22
Aug22
15
Required Output:
Month
Oct21
Nov21
Dec21
Jan22
Feb22
Mar22
Apr22
May22
Jun22
Jul22
Aug22
Bkt90
8
28
28
26
Bkt90-180
9
28
28
25
Bkt180+
10
28
28
35
15
With some transformations it could be done like below:
Your Sample Data
WITH
tbl_1 AS
(
Select To_Date('22-10-2021', 'dd-mm-yyyy') "DATE_1", To_Date('29-10-2021', 'dd-mm-yyyy') "DATE_2", 'Oct21' "MON", 8 "DAY_DIFF" From Dual Union All
Select To_Date('30-10-2021', 'dd-mm-yyyy') "DATE_1", To_Date('26-11-2021', 'dd-mm-yyyy') "DATE_2", 'Nov21' "MON", 28 "DAY_DIFF" From Dual Union All
Select To_Date('27-11-2021', 'dd-mm-yyyy') "DATE_1", To_Date('24-12-2021', 'dd-mm-yyyy') "DATE_2", 'Dec21' "MON", 28 "DAY_DIFF" From Dual Union All
Select To_Date('25-12-2021', 'dd-mm-yyyy') "DATE_1", To_Date('28-01-2022', 'dd-mm-yyyy') "DATE_2", 'Jan22' "MON", 35 "DAY_DIFF" From Dual Union All
Select To_Date('29-01-2022', 'dd-mm-yyyy') "DATE_1", To_Date('25-05-2022', 'dd-mm-yyyy') "DATE_2", 'Feb22' "MON", 28 "DAY_DIFF" From Dual Union All
Select To_Date('26-02-2022', 'dd-mm-yyyy') "DATE_1", To_Date('25-03-2022', 'dd-mm-yyyy') "DATE_2", 'Mar22' "MON", 28 "DAY_DIFF" From Dual Union All
Select To_Date('26-03-2022', 'dd-mm-yyyy') "DATE_1", To_Date('29-04-2022', 'dd-mm-yyyy') "DATE_2", 'Apr22' "MON", 35 "DAY_DIFF" From Dual Union All
Select To_Date('30-04-2022', 'dd-mm-yyyy') "DATE_1", To_Date('27-05-2022', 'dd-mm-yyyy') "DATE_2", 'May22' "MON", 28 "DAY_DIFF" From Dual Union All
Select To_Date('28-05-2022', 'dd-mm-yyyy') "DATE_1", To_Date('24-06-2022', 'dd-mm-yyyy') "DATE_2", 'Jun22' "MON", 28 "DAY_DIFF" From Dual Union All
Select To_Date('25-06-2022', 'dd-mm-yyyy') "DATE_1", To_Date('29-07-2022', 'dd-mm-yyyy') "DATE_2", 'Jul22' "MON", 35 "DAY_DIFF" From Dual Union All
Select To_Date('30-07-2022', 'dd-mm-yyyy') "DATE_1", To_Date('13-08-2022', 'dd-mm-yyyy') "DATE_2", 'Aug22' "MON", 15 "DAY_DIFF" From Dual
),
First Transformation (CTE transformed)
1st grouping (STEP)
Cumulativ Diff
Days Diff limitation and over the limits
transformed AS
(
SELECT
DATE_1,
DATE_2,
MON,
DAY_DIFF,
FLOOR(Sum(DAY_DIFF) OVER(PARTITION BY 1 ORDER BY DATE_1 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) / 90) "STEP",
Sum(DAY_DIFF) OVER(PARTITION BY 1 ORDER BY DATE_1 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) "CUM_DIFF",
CASE
WHEN Sum(DAY_DIFF) OVER(PARTITION BY 1 ORDER BY DATE_1 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) > 180
THEN DAY_DIFF - (Sum(DAY_DIFF) OVER(PARTITION BY 1 ORDER BY DATE_1 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) - 180)
WHEN Sum(DAY_DIFF) OVER(PARTITION BY 1 ORDER BY DATE_1 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) > 90
THEN DAY_DIFF - (Sum(DAY_DIFF) OVER(PARTITION BY 1 ORDER BY DATE_1 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) - 90)
ELSE 0
END "DAYS_LIMIT",
CASE
WHEN Sum(DAY_DIFF) OVER(PARTITION BY 1 ORDER BY DATE_1 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) > 180
THEN Sum(DAY_DIFF) OVER(PARTITION BY 1 ORDER BY DATE_1 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) - 180
WHEN Sum(DAY_DIFF) OVER(PARTITION BY 1 ORDER BY DATE_1 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) > 90
THEN Sum(DAY_DIFF) OVER(PARTITION BY 1 ORDER BY DATE_1 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) - 90
ELSE 0
END "DAYS_OVER"
FROM
tbl_1
),
Second Transformation (CTE buckets)
buckets AS
(
SELECT
DATE_1 "DATE_1",
DATE_2 "DATE_2",
MON "MON",
DAY_DIFF "DAY_DIFF",
CUM_DIFF "CUMM_DIFF",
CUM_DIFF * (STEP - FIRST_VALUE(STEP) OVER(PARTITION BY 1 ORDER BY DATE_1 ROWS BETWEEN 1 PRECEDING AND CURRENT ROW)) "CUM_DIFF_STEP",
STEP "STEP",
CASE WHEN DAYS_LIMIT <= 0 THEN DAY_DIFF ELSE DAYS_LIMIT END "DAYS_LIMIT",
FIRST_VALUE(STEP) OVER(PARTITION BY 1 ORDER BY DATE_1 ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) "CHG",
CASE
WHEN CUM_DIFF * (STEP - FIRST_VALUE(STEP) OVER(PARTITION BY 1 ORDER BY DATE_1 ROWS BETWEEN 1 PRECEDING AND CURRENT ROW)) = 0
THEN 0
ELSE
DAYS_OVER
END "DAYS_OVER"
FROM
transformed
ORDER BY DATE_1
),
So far the result is:
-- DATE_1 DATE_2 MON DAY_DIFF CUMM_DIFF CUM_DIFF_STEP STEP DAYS_LIMIT CHG DAYS_OVER
-- --------- --------- ----- ---------- ---------- ------------- ---------- ---------- ---------- ----------
-- 22-OCT-21 29-OCT-21 Oct21 8 8 0 0 8 0 0
-- 30-OCT-21 26-NOV-21 Nov21 28 36 0 0 28 0 0
-- 27-NOV-21 24-DEC-21 Dec21 28 64 0 0 28 0 0
-- 25-DEC-21 28-JAN-22 Jan22 35 99 99 1 26 0 9
-- 29-JAN-22 25-MAY-22 Feb22 28 127 0 1 28 1 0
-- 26-FEB-22 25-MAR-22 Mar22 28 155 0 1 28 1 0
-- 26-MAR-22 29-APR-22 Apr22 35 190 190 2 25 1 10
-- 30-APR-22 27-MAY-22 May22 28 218 0 2 28 2 0
-- 28-MAY-22 24-JUN-22 Jun22 28 246 0 2 28 2 0
-- 25-JUN-22 29-JUL-22 Jul22 35 281 281 3 35 2 101
-- 30-JUL-22 13-AUG-22 Aug22 15 296 0 3 15 3 0
Union All to create additional rows for overlapings (CTE buckets_all)
buckets_all AS
(
SELECT BKT, MON, DAYS_MON
FROM
(
SELECT
DATE_1 "DATE_1",
CASE
WHEN CHG = 0 THEN 'Bkt90'
WHEN CHG = 1 THEN 'Bkt180'
ELSE
'Bkt180+'
END "BKT",
MON "MON",
DAYS_LIMIT "DAYS_MON",
CHG "CHG"
FROM
buckets
UNION ALL
SELECT
DATE_1 "DATE_1",
CASE
WHEN CHG = 0 THEN 'Bkt180'
WHEN CHG = 1 THEN 'Bkt180+'
ELSE
'xxx'
END "BKT",
MON "MON",
DAYS_OVER "DAYS_MON",
CHG + 1 "CHG"
FROM
buckets
WHERE
DAYS_OVER > 0 And
CHG <= 1
ORDER BY DATE_1, CHG
)
)
Now we have the result that we need for final SQL (Pivoting)
-- BKT MON DAYS_MON
-- ------- ----- ----------
-- Bkt90 Oct21 8
-- Bkt90 Nov21 28
-- Bkt90 Dec21 28
-- Bkt90 Jan22 26
-- Bkt180 Jan22 9
-- Bkt180 Feb22 28
-- Bkt180 Mar22 28
-- Bkt180 Apr22 25
-- Bkt180+ Apr22 10
-- Bkt180+ May22 28
-- Bkt180+ Jun22 28
-- Bkt180+ Jul22 35
-- Bkt180+ Aug22 15
Main SQL Pivoting the data
SELECT
*
FROM
buckets_all
PIVOT
(
Max(DAYS_MON)
FOR MON IN('Oct21' "Oct21", 'Nov21' "Nov21", 'Dec21' "Dec21", 'Jan22' "Jan22", 'Feb22' "Feb22", 'Mar22' "Mar22", 'Apr22' "Apr22", 'May22' "May22", 'Jun22' "Jun22", 'Jul22' "Jul22", 'Aug22' "Aug22")
)
ORDER BY
CASE BKT
WHEN 'Bkt90' THEN 0
WHEN 'Bkt180' THEN 1
WHEN 'Bkt180+' THEN 2
END
R e s u l t
-- BKT Oct21 Nov21 Dec21 Jan22 Feb22 Mar22 Apr22 May22 Jun22 Jul22 Aug22
-- ------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
-- Bkt90 8 28 28 26
-- Bkt180 9 28 28 25
-- Bkt180+ 10 28 28 35 15
I wish count the rows of a table grouped by date and time intervals of 5 minutes:
for example, if the minutes portion of HH:MM falls between 00 mins and 04 min it will be counted as 00, eg. 08:04 will be counted as 08:00
if the minutes portion falls between 05 mins and 09 mins it will be counted as 05, eg. 08:06 will be counted as 08:05
Table Data
Date Time
18/01/18 08:00
18/01/18 08:01
18/01/18 08:02
18/01/18 08:03
18/01/18 08:04
18/01/18 08:05
18/01/18 08:06
18/01/18 08:08
18/01/18 08:10
19/01/18 17:01
19/01/18 17:03
19/01/18 17:04
Expected Output
DATE TIME COUNT
18/01/2018 08:00 5
18/01/2018 08:05 3
18/01/2018 08:10 1
19/01/2018 17:00 3
Table Creation
create table TAB1 (tDATE DATE,tTIME VARCHAR2(5));
Data
insert into TAB1(tDATE,tTIME) values (to_date('18/01/2018','DD/MM/YYYY'),'08:00');
insert into TAB1(tDATE,tTIME) values (to_date('18/01/2018','DD/MM/YYYY'),'08:01');
insert into TAB1(tDATE,tTIME) values (to_date('18/01/2018','DD/MM/YYYY'),'08:02');
insert into TAB1(tDATE,tTIME) values (to_date('18/01/2018','DD/MM/YYYY'),'08:03');
insert into TAB1(tDATE,tTIME) values (to_date('18/01/2018','DD/MM/YYYY'),'08:04');
insert into TAB1(tDATE,tTIME) values (to_date('18/01/2018','DD/MM/YYYY'),'08:05');
insert into TAB1(tDATE,tTIME) values (to_date('18/01/2018','DD/MM/YYYY'),'08:06');
insert into TAB1(tDATE,tTIME) values (to_date('18/01/2018','DD/MM/YYYY'),'08:08');
insert into TAB1(tDATE,tTIME) values (to_date('18/01/2018','DD/MM/YYYY'),'08:10');
insert into TAB1(tDATE,tTIME) values (to_date('19/01/2018','DD/MM/YYYY'),'17:01');
insert into TAB1(tDATE,tTIME) values (to_date('19/01/2018','DD/MM/YYYY'),'17:03');
insert into TAB1(tDATE,tTIME) values (to_date('19/01/2018','DD/MM/YYYY'),'17:04');
Whenever I need such interval I use this generic function:
CREATE OR REPLACE FUNCTION MakeInterval(ts IN TIMESTAMP, roundInterval IN INTERVAL DAY TO SECOND) RETURN TIMESTAMP DETERMINISTIC IS
denom INTEGER;
BEGIN
IF roundInterval >= INTERVAL '1' HOUR THEN
denom := EXTRACT(HOUR FROM roundInterval);
IF MOD(24, denom) <> 0 THEN
RAISE VALUE_ERROR;
END IF;
RETURN TRUNC(ts) + TRUNC(EXTRACT(HOUR FROM ts) / denom) * denom * INTERVAL '1' HOUR;
ELSIF roundInterval >= INTERVAL '1' MINUTE THEN
denom := EXTRACT(MINUTE FROM roundInterval);
IF MOD(60, denom) <> 0 THEN
RAISE VALUE_ERROR;
END IF;
RETURN TRUNC(ts, 'hh') + TRUNC(EXTRACT(MINUTE FROM ts) / denom) * denom * INTERVAL '1' MINUTE;
ELSE
denom := EXTRACT(SECOND FROM roundInterval);
IF MOD(60, denom) <> 0 THEN
RAISE VALUE_ERROR;
END IF;
RETURN TRUNC(ts, 'mi') + TRUNC(EXTRACT(SECOND FROM ts) / denom) * denom * INTERVAL '1' SECOND;
END IF;
END MakeInterval;
Valid intervals are: 1,2,3,4,5,6,10,12,15,20,30,60 SECOND, 1,2,3,4,5,6,10,12,15,20,30,60 MINUTE, 1,2,3,4,6,8,12 HOUR
You store time in a separate column which is a bad design. First, make a proper DATE or TIMESTAMP value, for example: TO_DATE(TO_CHAR(tDATE,'YYYY-MM-DD')||tTIME, 'YYYY-MM-DDHH24:MI')
Then you could use it like this
SELECT
MakeInterval(TO_DATE(TO_CHAR(tDATE,'YYYY-MM-DD')||tTIME, 'YYYY-MM-DDHH24:MI'), INTERVAL '5' MINUTE), ...
Of course, if you don't like to use a separate function you can put all in one line:
TRUNC(TO_DATE(TO_CHAR(tDATE,'YYYY-MM-DD')||tTIME, 'YYYY-MM-DDHH24:MI'), 'hh') + TRUNC(EXTRACT(MINUTE FROM TO_DATE(TO_CHAR(tDATE,'YYYY-MM-DD')||tTIME, 'YYYY-MM-DDHH24:MI')) / 5) * INTERVAL '5' MINUTE;
This is a bit cumbersome in Oracle, but quite feasible with string arithmetic:
select date,
substring(time, 1, 3) || lpad(floor(cast(substring(time, -2) as number) / 12) * 12, 2, '0') as time,
count(*)
from tab1
group by date,
substring(time, 1, 3) || lpad(floor(cast(substring(time, -2) as number) / 12) * 12, 2, '0')
order by date, time;
try this,
select tdate,
SUBSTR(ttime, 1, 2)||':'||
LPAD(NVL(DECODE(SIGN(ROUND(SUBSTR(ttime, 4, 2), -1)-SUBSTR(ttime, 4, 2)),
1, ROUND(SUBSTR(ttime, 4, 2), -1)-5,
-1, ROUND(SUBSTR(ttime, 4, 2), -1)), 0),2, '0') time_,
count(1)
from tab1
group by tdate, SUBSTR(ttime, 1, 2)||':'||
LPAD(NVL(DECODE(SIGN(ROUND(SUBSTR(ttime, 4, 2), -1)-SUBSTR(ttime, 4, 2)),
1, ROUND(SUBSTR(ttime, 4, 2), -1)-5,
-1, ROUND(SUBSTR(ttime, 4, 2), -1)), 0),2, '0');
Thank you all. As to why there is a separate VARCHAR2 column for the time component, the tables were originally migrated from some legacy database that had a date type but without a time component the latter which was stored as a string. Here is my own idea which gives me exactly what I want:
select tDATE,substr(tTIME,1,3)||
case
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 0 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 5 then '00'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 5 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 10 then '05'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 10 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 15 then '10'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 15 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 20 then '15'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 20 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 25 then '20'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 25 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 30 then '25'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 30 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 35 then '30'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 35 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 40 then '35'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 40 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 45 then '40'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 45 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 50 then '45'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 50 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 55 then '50'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 55 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 60 then '55'
else '00'
end as tTIME
,count(*)
from TAB1
group by tDATE,substr(tTIME,1,3)||
case
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 0 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 5 then '00'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 5 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 10 then '05'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 10 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 15 then '10'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 15 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 20 then '15'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 20 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 25 then '20'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 25 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 30 then '25'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 30 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 35 then '30'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 35 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 40 then '35'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 40 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 45 then '40'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 45 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 50 then '45'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 50 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 55 then '50'
when to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) >= 55 and to_number(regexp_substr(substr(tTIME,4,2),'^[-]?[[:digit:]]*\.?[[:digit:]]*$')) < 60 then '55'
else '00'
end
order by 3 desc;
SELECT pd_end_dt,SUM(nrx_cnt) Total_Count
FROM wkly_lnd.lnd_wkly_plan_rx_summary
WHERE pd_end_dt >= '01-Sep-08' AND pd_end_dt < '30-Sep-08'
GROUP BY pd_end_dt
SELECT pd_end_dt,SUM(nrx_cnt) Total_Count
FROM wkly_lnd.lnd_wkly_plan_rx_summary
WHERE pd_end_dt >= '01-Sep-07' AND pd_end_dt < '30-Sep-07'
GROUP BY pd_end_dt
the result set on running each query will be like
09/28/2007 00:00:00 702,457.36
09/21/2007 00:00:00 703,604.59
09/07/2007 00:00:00 636,619.92
09/14/2007 00:00:00 698,082.03
similarly for previous year
I need to calculate the difference of units sold as compared to last year and also to add one column which will find percentage change
There are a lot of things unsaid. I hope you receive clearer requirements in your daily work ...
Anyway, here is a simulation of your situation. It's based on the assumption that the days with data (one per week) are the same in 2007 as in 2008:
SQL> create table lnd_wkly_plan_rx_summary (pd_end_dt,nrx_cnt)
2 as
3 select date '2008-09-07', 100000 from dual union all
4 select date '2008-09-07', 536619.92 from dual union all
5 select date '2008-09-14', 698082.03 from dual union all
6 select date '2008-09-21', 403604.59 from dual union all
7 select date '2008-09-21', 200000 from dual union all
8 select date '2008-09-21', 100000 from dual union all
9 select date '2008-09-28', 702457.36 from dual union all
10 select date '2007-09-07', 400000 from dual union all
11 select date '2007-09-14', 450000 from dual union all
12 select date '2007-09-21', 500000 from dual union all
13 select date '2007-09-28', 550000 from dual union all
14 select date '2007-09-28', 100000 from dual
15 /
Tabel is aangemaakt.
And your original queries, slightly modified.
SQL> SELECT pd_end_dt
2 , SUM(nrx_cnt) Total_Count
3 FROM lnd_wkly_plan_rx_summary
4 WHERE pd_end_dt >= date '2008-09-01'
5 AND pd_end_dt < date '2008-09-30'
6 GROUP BY pd_end_dt
7 /
PD_END_DT TOTAL_COUNT
------------------- -----------
07-09-2008 00:00:00 636619,92
14-09-2008 00:00:00 698082,03
21-09-2008 00:00:00 703604,59
28-09-2008 00:00:00 702457,36
4 rijen zijn geselecteerd.
SQL> SELECT pd_end_dt
2 , SUM(nrx_cnt) Total_Count
3 FROM lnd_wkly_plan_rx_summary
4 WHERE pd_end_dt >= date '2007-09-01'
5 AND pd_end_dt < date '2007-09-30'
6 GROUP BY pd_end_dt
7 /
PD_END_DT TOTAL_COUNT
------------------- -----------
07-09-2007 00:00:00 400000
14-09-2007 00:00:00 450000
21-09-2007 00:00:00 500000
28-09-2007 00:00:00 650000
4 rijen zijn geselecteerd.
And the query with which you can compare the 2007 and 2008 data:
SQL> select to_char(pd_end_dt,'dd-mm') day_and_month
2 , sum(case trunc(pd_end_dt,'yyyy') when date '2007-01-01' then nrx_cnt end) sum2007
3 , sum(case trunc(pd_end_dt,'yyyy') when date '2008-01-01' then nrx_cnt end) sum2008
4 , sum(case trunc(pd_end_dt,'yyyy') when date '2008-01-01' then nrx_cnt end)
5 - sum(case trunc(pd_end_dt,'yyyy') when date '2007-01-01' then nrx_cnt end) difference
6 , ( sum(case trunc(pd_end_dt,'yyyy') when date '2008-01-01' then nrx_cnt end)
7 - sum(case trunc(pd_end_dt,'yyyy') when date '2007-01-01' then nrx_cnt end)
8 ) / sum(case trunc(pd_end_dt,'yyyy') when date '2008-01-01' then nrx_cnt end) * 100 percentage_difference
9 from lnd_wkly_plan_rx_summary
10 where ( ( pd_end_dt >= date '2007-09-01'
11 and pd_end_dt < date '2007-09-30'
12 )
13 or ( pd_end_dt >= date '2008-09-07'
14 and pd_end_dt < date '2008-09-30'
15 )
16 )
17 group by to_char(pd_end_dt,'dd-mm')
18 /
DAY_A SUM2007 SUM2008 DIFFERENCE PERCENTAGE_DIFFERENCE
----- ---------- ---------- ---------- ---------------------
07-09 400000 636619,92 236619,92 37,1681615
14-09 450000 698082,03 248082,03 35,5376617
21-09 500000 703604,59 203604,59 28,9373595
28-09 650000 702457,36 52457,36 7,46769313
4 rijen zijn geselecteerd.
Although rather verbose, I think it speaks for itself.
You may like the following rewrite, since it doesn't repeat the aggregate functions as much as in the query above:
SQL> select day_and_month
2 , sum2007
3 , sum2008
4 , sum2008-sum2007 difference
5 , 100*(sum2008-sum2007)/sum2008 percentage_difference
6 from ( select to_char(pd_end_dt,'dd-mm') day_and_month
7 , sum(case trunc(pd_end_dt,'yyyy') when date '2007-01-01' then nrx_cnt end) sum2007
8 , sum(case trunc(pd_end_dt,'yyyy') when date '2008-01-01' then nrx_cnt end) sum2008
9 from lnd_wkly_plan_rx_summary
10 where ( pd_end_dt >= date '2007-09-01'
11 and pd_end_dt < date '2007-09-30'
12 )
13 or ( pd_end_dt >= date '2008-09-07'
14 and pd_end_dt < date '2008-09-30'
15 )
16 group by to_char(pd_end_dt,'dd-mm')
17 )
18 /
DAY_A SUM2007 SUM2008 DIFFERENCE PERCENTAGE_DIFFERENCE
----- ---------- ---------- ---------- ---------------------
07-09 400000 636619,92 236619,92 37,1681615
14-09 450000 698082,03 248082,03 35,5376617
21-09 500000 703604,59 203604,59 28,9373595
28-09 650000 702457,36 52457,36 7,46769313
4 rijen zijn geselecteerd.
Hope this helps.
Regards,
Rob.
if you want to compare the results of the query year-on-year (ie for each day with the day of the preceeding year), you can group by the day of the year to_char('dd-mon'):
SQL> WITH lnd_wkly_plan_rx_summary AS (
2 SELECT DATE '2007-09-28' pd_end_dt, 702457.36 nrx_cnt FROM dual
3 UNION ALL SELECT DATE '2007-09-21', 703604.59 FROM dual
4 --
5 UNION ALL SELECT DATE '2008-09-28' pd_end_dt, 702557.36 nrx_cnt FROM dual
6 UNION ALL SELECT DATE '2008-09-21', 703404.59 FROM dual
7 )
8 SELECT to_char(pd_end_dt, 'dd-mon') pd_end_dt,
9 SUM(CASE
10 WHEN to_char(pd_end_dt, 'yyyy') = '2007' THEN
11 nrx_cnt
12 END) Total_2007,
13 SUM(CASE
14 WHEN to_char(pd_end_dt, 'yyyy') = '2008' THEN
15 nrx_cnt
16 END) Total_2008,
17 SUM(CASE
18 WHEN to_char(pd_end_dt, 'yyyy') = '2008' THEN
19 nrx_cnt
20 ELSE
21 -nrx_cnt
22 END) delta
23 FROM lnd_wkly_plan_rx_summary
24 WHERE ((pd_end_dt >= DATE '2007-09-01' AND pd_end_dt < DATE '2007-09-30') OR
25 (pd_end_dt >= DATE '2008-09-01' AND pd_end_dt < DATE '2008-09-30'))
26 GROUP BY to_char(pd_end_dt, 'dd-mon');
PD_END_DT TOTAL_2007 TOTAL_2008 DELTA
------------ ---------- ---------- ----------
28-sep 702457,36 702557,36 100
21-sep 703604,59 703404,59 -200
Why dont you look at the analytic functions provided with Oracle? I am assuming you are using Oracle as you have tagged your question with the Oracle tag. You can refer to http://www.oracle-base.com/articles/misc/LagLeadAnalyticFunctions.php.
I am simplifying your data set to look like this
09/08/2007 100
09/08/2008 200
09/09/2007 350
09/09/2008 400
09/10/2007 150
09/10/2008 175
These are your total counts on the 8,9 and 10th of September in the years 2007 and 2008
You can use the following query:
Assuming table to be T(end_date,cnt) (your names are too long! sorry )
Select end_date, cnt,
LAG(cnt,1,0) over (order by
to_number(to_char(end_dt,'dd')),to_number(to_char(end_dt,'mm'))) cntPrev,
cnt - LAG(cnt,1,0) over (order by
to_number(to_char(end_dt,'dd')),to_number(to_char(end_dt,'mm'))) cntDiff
from T
In simpler terms(this will not work if you copy, paste).
Let X=LAG(cnt,1,0) over (order by
to_number(to_char(end_dt,'dd')),to_number(to_char(end_dt,'mm')))
Your query is
Select end_date, cnt, X cntPrev, cnt-X cntDiff
from T;