How to extract random dates targets/sales data from monthly targets - sql

I have monthly targets defined for the different category of items for the complete year.
Example:
January Target for A Category - 15,000
January Target for R Category - 10,000
January Target for O Category - 5,000
Actual Sales for A Category January - 18,400
Actual Sales for R Category January - 8,500
Actual Sales for O Category January - 3,821
The SQL query to compare actual sales with target will be simple as follows:
SELECT TO_CHAR (Sales_Date, 'MM') Sales_Month,
Sales_Category,
SUM (Sales_Value) Sales_Val_Monthly,
Target_Month,
Target_Category,
Target_Value
FROM Sales_Data, Target_Data
WHERE TO_CHAR (Sales_Date, 'MM') = Target_Month
AND Sales_Category = Target_Category
GROUP BY TO_CHAR (Sales_Date, 'MM'),
Target_Month,
Target_Category,
Sales_Category,
Target_Value;
Now I have a requirement that user will input FROM_DATE and TILL_DATE in the report parameter and the starting/ending date can be random, it will not represent a complete month or week, the start date can be 12/01/2018 and end date can be 15/01/2018, i.e., data for 4 days. The result should calculate the actual data for 4 days, calculate the target for 4 days considering the fact that there will be 6 working days (Sunday is a holiday) and if the date range includes Sunday, it should not be considered.
Also, the number of days in a month should be considered and the date parameters may contain some days from one month and some days from another month or maybe more than one month.
Target_Table (Target_Data)
Target_Year Target_Month Target_Category Target_Value
2018 01 A 15000
2018 02 A 8500
2018 03 A 9500
2018 01 R 15000
2018 02 R 8500
2018 03 R 9500
2018 01 O 15000
2018 02 O 8500
2018 03 O 9500
Sales Table (Sales_Data)
Inv_Txn Inv_No Sales_Date Item_Code Sales_Category Qty Rate Sales_Value Inv_Locn Inv_SM_ID
A21 2018000001 02/01/2018 XXXX A 2 5.5 11 O001 XXXX
R32 2018000001 27/02/2018 XXXX R 3 9.5 28.5 O305 XXXX
O98 2018000001 12/03/2018 XXXX O 12 12.5 150 O901 XXXX
U76 2018000001 18/01/2018 XXXX A 98 5.5 539 O801 XXXX
B87 2018000001 19/02/2018 XXXX R 2 9.5 19 O005 XXXX
A21 2018000002 13/03/2018 XXXX R 45 9.5 427.5 O001 XXXX
B87 2018000002 14/03/2018 XXXX O 12 12.5 150 O005 XXXX
Desired Output (From Date: 27/02/2018 Till Date: 06/03/2018)
Target_Category Target_Value Sales_Value
A 87.52 21.88
A 96.25 24.06
A 74.25 18.56
R 100.25 25.06
R 800.2 200.05
R 25.1 6.28
O 75.5 18.88
O 98.1 24.53
O 25.5 6.38

The first step might be to see whether we can get the number of Sundays in a given month. As it turns out, we can - and we don't have to use any SQL tricks or PL/SQL:
SELECT EXTRACT( DAY FROM LAST_DAY(SYSDATE) ) AS month_day_cnt
, CEIL( ( LAST_DAY(TRUNC(SYSDATE, 'MONTH')) - NEXT_DAY(TRUNC(SYSDATE, 'MONTH')-1, 'SUN') + 1 ) / 7 ) AS sunday_cnt
FROM dual;
This will give us the number of days in a given month as well as the number of Sundays. All we need to do is subtract the latter number from the former to get the number of working days. We can work that into your initial query (by the way, I suggest using TRUNC() instead of TO_CHAR() since your users might want a date range that spans more than one calendar year):
SELECT TRUNC(s.Sales_Date, 'MONTH') AS Sales_Month
, EXTRACT( DAY FROM LAST_DAY( TRUNC(s.Sales_Date, 'MONTH') ) ) - CEIL( ( LAST_DAY(TRUNC(s.Sales_Date, 'MONTH')) - NEXT_DAY(TRUNC(s.Sales_Date, 'MONTH')-1, 'SUN') + 1 ) / 7 ) AS working_day_cnt
, s.Sales_Category, SUM(s.Sales_Value) AS Sales_Val_Monthly
, t.Target_Value -- Target_Month and Target_Category are superfluous
FROM Sales_Data s INNER JOIN Target_Data t
ON TO_CHAR(s.Sales_Date, 'MM') = t.Target_Month
AND TO_CHAR(s.Sales_Date, 'YYYY') = t.Target_Year
AND s.Sales_Category = t.Target_Category
GROUP BY TRUNC(s.Sales_Date, 'MONTH'), Sales_Category, Target_Value;
Now given a start date and an end date, we can generate the number of working days for all the months in between those dates as follows:
SELECT TRUNC(range_dt, 'MONTH'), COUNT(*) FROM (
SELECT start_dt + LEVEL - 1 AS range_dt
FROM dual
CONNECT BY start_dt + LEVEL - 1 < end_dt
) WHERE TO_CHAR(range_dt, 'DY') != 'SUN'
GROUP BY TRUNC(range_dt, 'MONTH');
where start_dt and end_dt are parameters supplied by the user. Putting this all together, we'll have something like the following:
WITH rd ( range_month, range_day_cnt ) AS (
SELECT TRUNC(range_dt, 'MONTH'), COUNT(*) FROM (
SELECT start_dt + LEVEL - 1 AS range_dt
FROM dual
CONNECT BY start_dt + LEVEL - 1 < end_dt
) WHERE TO_CHAR(range_dt, 'DY') != 'SUN'
GROUP BY TRUNC(range_dt, 'MONTH')
)
SELECT range_month, Sales_Category, Sales_Val_Monthly
, range_day_cnt, working_day_cnt, Target_Value
, Target_Value*range_day_cnt/working_day_cnt AS prorated_target_value
FROM (
SELECT r.range_month, r.range_day_cnt
, EXTRACT( DAY FROM LAST_DAY( TRUNC(s.Sales_Date, 'MONTH') ) ) - CEIL( ( LAST_DAY(TRUNC(s.Sales_Date, 'MONTH')) - NEXT_DAY(TRUNC(s.Sales_Date, 'MONTH')-1, 'SUN') + 1 ) / 7 ) AS working_day_cnt
, s.Sales_Category, SUM(s.Sales_Value) AS Sales_Val_Monthly
, t.Target_Value -- Target_Month and Target_Category are superfluous
FROM rd INNER JOIN Sales_Data s
ON rd.range_month = TRUNC(s.Sales_Date, 'MONTH')
INNER JOIN Target_Data t
ON TO_CHAR(s.Sales_Date, 'MM') = t.Target_Month
AND TO_CHAR(s.Sales_Date, 'YYYY') = t.Target_Year
AND s.Sales_Category = t.Target_Category
WHERE s.Sales_Date >= TRUNC(start_dt)
AND s.Sales_Date < TRUNC(end_dt+1)
GROUP BY r.range_month, r.range_day_cnt, s.Sales_Category, t.Target_Value
) ORDER BY range_month;
If you have a table of public holidays, then those will have to be factored in somewhere as well - both in the rd common table expression and from the calculation of working days. If the above doesn't give you a start on that then I can take a look again in a bit and see how the other holidays might be worked in.

You can calculate the number of working days between two dates using below query. I added a nonworking date via a table named: holiday_dates and created a series of dates from 12/01/2018 to 15/01. I remove those dates that are either Sunday or holiday. Please let me know if it works for you. Thanks.
create table holiday_dates(holiday_dte date, holiday_desc varchar(100));
insert into holiday_dates values(TO_DATE('13/01/2018','DD-MM-YYYY'), 'Not a Working Day');
With tmp as (
select count(*) as num_of_working_days
from ( select rownum as rn
from all_objects
where rownum <= to_date('15/01/2018','DD-MM-YYYY') - to_date('12/01/2018','DD-MM-YYYY')+1 )
where to_char( to_date('12/01/2018','DD-MM-YYYY')+rn-1, 'DY' ) not in ( 'SUN' )
and not exists ( select null from holiday_dates where holiday_dte = trunc(to_date('12/01/2018','DD-MM-YYYY') + rn - 1)))
SELECT TO_CHAR (Sales_Date, 'MM') Sales_Month,
Sales_Category,
SUM (Sales_Value) Sales_Val_Monthly,
Target_Month,
Target_Category,
Target_Value,
tmp.num_of_working_days
FROM Sales_Data, Target_Data, tmp
WHERE Sales_Date between to_date('12/01/2018','DD-MM-YYYY') and to_date('15/01/2018','DD-MM-YYYY')
AND Sales_Category = Target_Category
GROUP BY TO_CHAR (Sales_Date, 'MM'),
Target_Month,
Target_Category,
Sales_Category,
Target_Value;

Related

Split single row into multiple rows based on week of the month in date

I have a table like this
Id Valid_From Valid_To
9744 24/06/2019 07/07/2019
9745 12/08/2019 31/12/9999
I would like to split this table into multiple rows based on the week like this by joining to the date table
Id Valid_from Valid_To Month Week
9744 24/06/2019 07/07/2019 June 4
9744 24/06/2019 07/07/2019 July 1
9744 24/06/2019 07/07/2019 July 2
9745 12/08/2019 31/12/9999 August 2
9745 12/08/2019 31/12/9999 August 3
9745 12/08/2019 31/12/9999 August 4
In this case there will be 3 rows as the valid from and valid two falls between these 3 weeks for ID - 9744
For ID - 9745 the Valid_to date is infinity so we need to just take all the week in the current month from the valid_from date
I then just need to append the output with Month and the Week number
Can someone help me to write a query to have this output?
Thanks
You mention a "date" table. If you have one then you can use a join like this:
select distinct t.id, valid_from, t.valid_to, d.month, d.week
from yourtable t join
date d
on d.date >= t.valid_from and
d.date <= t.valid_to;
If I understand your question right, you need to list all month names and week numbers of these months' existing between valid_from and valid_to dates. I did it by following query:
SELECT
Q.ID,
Q.VALID_FROM,
Q.VALID_TO,
Q.MONTH_NAME,
WEEK_NUMBER
FROM
(
SELECT
CEIL((Q.DATES_BETWEEN_INTERVAL - FIRST_DAY_OF_MONTH + 1) / 7) WEEK_NUMBER,
TO_CHAR(Q.DATES_BETWEEN_INTERVAL, 'MONTH', 'NLS_DATE_LANGUAGE = American') MONTH_NAME,
Q.*
FROM
(
SELECT
LEVEL + S.VALID_FROM DATES_BETWEEN_INTERVAL,
TRUNC(LEVEL + S.VALID_FROM, 'MONTH') FIRST_DAY_OF_MONTH,
S.* FROM
(
SELECT T.*,
(CASE WHEN EXTRACT(YEAR FROM T.VALID_TO) = 9999 THEN LAST_DAY(T.VALID_FROM) ELSE T.VALID_TO END) - T.VALID_FROM DAYS_COUNT
FROM AAA_TABLE T
) S
CONNECT BY LEVEL <= S.DAYS_COUNT
) Q
) Q
GROUP BY
Q.ID,
Q.VALID_FROM,
Q.VALID_TO,
Q.MONTH_NAME,
WEEK_NUMBER
ORDER BY
Q.ID,
Q.VALID_FROM,
Q.VALID_TO,
Q.MONTH_NAME,
WEEK_NUMBER;
But there must be 5th week if the date greater than 28th day of month. Hope this will help you.

SQL script to show current week and first week of every month

I have written sql code to pull all the data that I need but the startdate is in weeks. I need to see the current week and then for future dates I need to see the first date of each month. (Like I said these are in weeks and the date is each Monday. (ex. 03-JUN-19, 10-JUN-19, 17-JUN-19....)
select
d.ITEM,
i.DESCRIPTION,
d.MARKET,
(CASE
WHEN d.LOC like 'U%' THEN 'US'
WHEN d.LOC like 'M%' THEN 'MX'
WHEN d.LOC like 'C%' THEN 'CA'
ELSE 'EXP' END) as COUNTRY,
d.START_DATE as STARTDATE,
SUM(d.DEMANDQTY) as QTY
from DEMAND d, ITEM i
where d.ITEM = i.ITEM
GROUP BY d.ITEM, i.DESCRIPTION, d.MARKET, d.LOC,d.START_DATE
So if I pulled this data today (7-JUN-19) I would need to only pull dates that are...03-JUN-19, 01-JUL-19, 05-AUG-19, 02-SEP-19, 07-OCT-19, 04-NOV-19, 02-DEC-19, 06-JAN-20 on and on until the data is done.
You need a calendar which will return desired dates - the first Monday (that follows today), and then the first Monday in every month. Here's how:
SQL> with
2 this_year as
3 (select trunc(sysdate) + level - 1 datum
4 from dual
5 connect by level <= date '2019-12-31' - trunc(sysdate) + 1
6 ),
7 formatted as
8 (select datum,
9 to_char(datum, 'd') day_num,
10 to_char(datum, 'dy', 'nls_date_language = english') day_name
11 from this_year
12 )
13 select *
14 from formatted f
15 where 1 = 1
16 and f.datum = (select min(f1.datum)
17 from formatted f1
18 where f1.day_num = 1
19 and to_char(f1.datum, 'yyyymm') = to_char(f.datum, 'yyyymm')
20 );
DATUM D DAY_NAME
---------- - ------------
10.06.2019 1 mon
01.07.2019 1 mon
05.08.2019 1 mon
02.09.2019 1 mon
07.10.2019 1 mon
04.11.2019 1 mon
02.12.2019 1 mon
7 rows selected.
SQL>
What does it do?
this_year CTE returns all dates from "today" to Dec 31st 2019. If there's some MAX date you can use - use it
formatted CTE is an intermediate step; if you run it, you'll see that day_num represents ordinal number of a day (1, 2, ... 7), while day_name returns their abbreviate names (mon, tue, ...). We are interested in day number 1 as it represents Mondays
the final select returns desired result - the first Monday in every month
Once you have those dates, use them in your current query. How? A simple option is to create a view based on such a query, e.g.
SQL> create or replace view v_mondays as
2 with
3 this_year as ...
<snip>
View created.
SQL> select * From v_mondays;
DATUM D DAY_NAME
---------- - ------------
10.06.2019 1 mon
01.07.2019 1 mon
<snip>
7 rows selected.
SQL>
Expanded, your query might look like this:
select
d.item,
i.description,
d.market,
case
when d.loc like 'U%' then 'US'
when d.loc like 'M%' then 'MX'
when d.loc like 'C%' then 'CA'
else 'EXP'
end as country,
d.start_date as startdate,
sum(d.demandqty) as qty
from demand d join item i on d.item = i.item
join v_mondays m on m.datum = d.start_date --> join with Mondays
group by d.item, i.description, d.market, d.loc,d.start_date;

SQL query to get current and last year sales

I have the following table Sales:
Date Store Sales
1/1/2015 St01 12123
1/1/2015 St02 3123
1/1/2016 St01 4213
1/1/2016 St03 2134
When I try to self join to get this year and last year sales the closed store is not showing up.
The result should be like this:
Date Store This year Sales Last Year Sales
1/1/2016 St01 4213 1212
1/1/2016 St02 0 3123
1/1/2016 St03 2134 0
My query as follows:
SELECT CY.DATE,
CY.store cy.Sales,
LY.sales
FROM sales CY,
sales LY
WHERE CY.store(+) = LY.store(+)
AND LY.DATE = CY.DATE - 365
Oracle Setup:
CREATE TABLE sales ( "DATE", Store, Sales ) AS
SELECT DATE '2015-01-01', 'St01', 12123 FROM DUAL UNION ALL
SELECT DATE '2015-01-01', 'St02', 3123 FROM DUAL UNION ALL
SELECT DATE '2016-01-01', 'St01', 4213 FROM DUAL UNION ALL
SELECT DATE '2016-01-01', 'St03', 2134 FROM DUAL;
Query:
SELECT TRUNC( SYSDATE, 'YY' ) AS "DATE",
Store,
SUM( CASE WHEN "DATE" = TRUNC( SYSDATE, 'YY' )
THEN sales END )
AS "This year sales",
SUM( CASE WHEN "DATE" = ADD_MONTHS( TRUNC( SYSDATE, 'YY' ), -12 )
THEN sales END )
AS "Last year sales"
FROM sales
GROUP BY store
ORDER BY store;
Output:
DATE STORE This year sales Last year sales
------------------- ----- --------------- ---------------
2016-01-01 00:00:00 St01 4213 12123
2016-01-01 00:00:00 St02 3123
2016-01-01 00:00:00 St03 2134
What you need is called Pivoting Table. Although Oracle has specific clauses to do it, you can use just plain and pure SQL to do so, like this:
SELECT store,
SUM(CASE WHEN Extract(year FROM DATE) = Extract(year FROM SYSDATE) THEN
sales
ELSE 0
END) AS "This year Sales",
SUM(CASE WHEN Extract(year FROM DATE) = Extract(year FROM SYSDATE) - 1 THEN
sales
ELSE 0
END) AS "Last year Sales"
FROM sales
WHERE Extract(year FROM DATE) >= Extract(year FROM SYSDATE) - 1
GROUP BY store
ORDER BY store
It would show:
Store This year Sales Last year Sales
St01 4213 12123
St02 0 3123
St03 2134 0
Note that makes no sense to have to column date as the first column. You couldn't group by it to show the output you want.
See the equivalent of this query here on fiddle: http://sqlfiddle.com/#!15/7662d8/6
Since I want the query to return day by day sales I used MT0 answer and added the dates, this way I can get the data for all year days.
WITH AllYear AS
(select to_date('2016-01-01', 'yyyy-mm-dd') + level - 1 AS dobs
from dual
connect by level <= 366)
SELECT dobs AS "DATE",
Store,
nvl(SUM(CASE
WHEN t.Date = dobs THEN
t.sales
END),
0) AS "This Year Sales",
nvl(SUM(CASE
WHEN t.Date = dobs-365 THEN
t.sales
END),
0) AS "Last Year Sales"
FROM Sales t,AllYear
where dobs='01-Jan-2016'
GROUP BY Store
ORDER BY Store;
The general solution is a full outer join, which includes all records from both joined tables. I don't know the Oracle syntax, but in MS SQL Server it would be something like this:
SELECT ISNULL(CY.DATE, LY.DATE) as DATE,
ISNULL(CY.store, LY.store) as STORE,
isnull(cy.Sales, 0),
isnull(LY.sales, 0)
FROM sales CY FULL OUTER JOIN sales LY
ON CY.store = LY.store
AND (CY.DATE IS NULL OR
DATEPART(year, LY.DATE) = DATEPART(year, CY.DATE) - 1
ISNULL(a, b) gives a if A IS NOT NULL, else b. DATEPART extracts specified part of a date; I'm comparing a difference of exactly one year rather than 365 days, in case "last year" is a leap year/
I only work with SQL Server. If anything is different, try to apply the same logic.
Declaring a temporary table to test the query:
DECLARE #Sales TABLE (
[Date] DATE,
Store NVARCHAR(10),
Sales INT
)
INSERT INTO #Sales VALUES
('1/1/2015','St01',12123),
('1/1/2015','St02',3123),
('1/1/2016','St01',4213),
('1/1/2016','St03',2134);
SELECT * FROM #Sales;
The actual query:
SELECT
CY_Date = CASE
WHEN CY.Date IS NULL THEN DATEADD(YEAR, 1, LY.Date)
ELSE CY.Date
END,
LY_Date = CASE
WHEN LY.Date IS NULL THEN DATEADD(YEAR, -1, CY.Date)
ELSE LY.Date
END,
Store = CASE
WHEN CY.Store IS NULL THEN LY.Store
ELSE CY.Store
END,
ISNULL(CY.Sales, 0) AS CY_Sales,
ISNULL(LY.Sales, 0) AS LY_Sales
FROM #Sales CY
FULL JOIN #Sales LY ON (CY.Store = LY.Store AND LY.Date = DATEADD(YEAR, -1, CY.Date))
WHERE (CY.Date = '1/1/2016' OR CY.Date IS NULL)
AND (LY.Date = DATEADD(YEAR, -1, '1/1/2016') OR LY.Date IS NULL);
Result:
CY_Date LY_Date Store CY_Sales LY_Sales
2016-01-01 2015-01-01 St01 4213 12123
2016-01-01 2015-01-01 St03 2134 0
2016-01-01 2015-01-01 St02 0 3123
How it works:
The FULL JOIN will will combine by the Store and the lines from the current and the year before.
The WHERE clause will filter by the current date '1/1/2016'. The NULLs are allowed because sometimes you don't have lines for the current or for the last year.
On the columns, CASES are used to create the dates if they are null (If the current date is null, get the last year + 1 year, and vice versa), to create the store if they are null and to place a zero instead of a null on the sales columns.

Counting the number of days excluding sunday between two dates

I am trying to calculate number of days betwen two dates excluding sundays. This is my query,
SELECT F_PLANHM_END_DT
- F_PLANHM_ST_DT
- 2
* (TO_CHAR (F_PLANHM_END_DT, 'WW') - TO_CHAR (F_PLANHM_ST_DT, 'WW'))
FROM VW_S_CURV_PROC
WHERE HEAD_MARK = 'IGG-BLH-BM 221';
SELECT COUNT (*)
FROM (SELECT SYSDATE + l trans_date
FROM ( SELECT LEVEL - 1 l
FROM VW_S_CURV_PROC
CONNECT BY LEVEL <= ( (SYSDATE + 7) - SYSDATE)))
WHERE TO_CHAR (trans_date, 'dy') NOT IN ('sun');
I am retrieving date from a view called VW_S_CURV_PROC with start date : F_PLANHM_ST_DT and end date F_PLANHM_END_DT. Somehow i cant make this to work. Please help me...
You could use the ROW GENERATOR technique to first generate the dates for a given range, and then exclude the SUNDAYs.
For example, this query will give me the total count of days between 1st Jan 2014 and 31st Dec 2014, excluding the Sundays -
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') <> 'SUN'
11 THEN 1
12 ELSE 0
13 END holiday
14 FROM data
15 CONNECT BY LEVEL <= date2-date1+1
16 )
17 /
HOLIDAY_COUNT
-------------
313
SQL>

return zero when grouping by week after select -

I have some code below
select
'WEEK '|| row_number() over(order by trunc(NEXT_DAY(to_date(substr(LOGDATE,1,10), 'DD-MM-YY'),'SATURDAY'), 'IW')) WEEK,
count(*)
from opencall
where trunc(to_date(substr(LOGDATE,1,10), 'DD-MM-YY')) BETWEEN '01-JAN-14' AND '31-DEC-14'
group by trunc(NEXT_DAY(to_date(substr(LOGDATE,1,10), 'DD-MM-YY'),'SATURDAY'), 'IW')
order by trunc(NEXT_DAY(to_date(substr(LOGDATE,1,10), 'DD-MM-YY'),'SATURDAY'), 'IW');
Which is a great piece of code that another stackoverflow chap wrote for me for extracting calls from the database and counting / grouping them by week and it works perfectly if there are always calls in every week.
However I've tried reusing this code for other uses and if there is no result in a given week I get fewer groupings i.e. perhaps only 37 weeks returned instead of the current 50 weeks for instance.
As this is removing the weeks where there are no calls the week numbers dont equate to real week numbers in the year. So I don't actually know what weeks the calls are being returned from as the weeks remain sequential rather than having zeros in the weeks with no calls.
Is there any way to return a zero value if there are no results returned for a given week? I imagine that may make the code much more complex.
If I am using this code over a week
e.g:
WEEK 1 231
WEEK 2 657
WEEK 3 529
WEEK 4 606
WEEK 5 594
WEEK 6 517
WEEK 7 683
WEEK 8 664
WEEK 9 583
WEEK 10 740
If you want to know which week you are showing, then why the fuss with row_number()?
This gives you the year separated in weeks, starting with the first seven days make week 1 and so on:
select to_char(mydate, 'ww'), count(*)
from
(
select to_date(substr(logdate,1,10), 'dd-mm-yy') as mydate
from opencall
)
where extract(year from mydate) = 2014
group by to_char(mydate, 'ww')
order by to_char(mydate, 'ww');
(Why is LOGDATE a string? And why are its first 10 characters in the format 'DD-MM-YY' which is 8 characters long?)
select
'WEEK '|| row_number() over(order by trunc(NEXT_DAY(to_date(substr(LOGDATE,1,10), 'DD-MM-YY'),'SATURDAY'), 'IW')) WEEK,
row_number() over(order by trunc(NEXT_DAY(to_date(substr(LOGDATE,1,10), 'DD-MM-YY'),'SATURDAY'), 'IW')) mWeek,
count(*)
from (SELECT to_char(SYSDATE - (LEVEL * 7),'IW') TheDate
FROM dual
CONNECT BY LEVEL <= 52) WoY
LEFT JOIN opencall
on Woy.TheDate = OpenCall.mWeek
where trunc(to_date(substr(LOGDATE,1,10), 'DD-MM-YY')) BETWEEN '01-JAN-14' AND '31-DEC-14'
group by trunc(NEXT_DAY(to_date(substr(LOGDATE,1,10), 'DD-MM-YY'),'SATURDAY'), 'IW')
order by trunc(NEXT_DAY(to_date(substr(LOGDATE,1,10), 'DD-MM-YY'),'SATURDAY'), 'IW');
Something like this may work.
I tried something like this with test data and works. I hope this works for you too:
select
'WEEK '|| row_number() over(order by nvl(to_char(NEXT_DAY(to_date(substr(LOGDATE,1,10), 'DD-MM-YY') ,'SATURDAY'), 'IW'),week)) WEEK
,count(wk)
from (select t.*,to_char(to_date(substr(LOGDATE,1,10), 'DD-MM-YY'),'IW') wk,w.week from
table1 t right join (SELECT lpad(level,2,0) week
FROM dual
CONNECT BY LEVEL <= 52 ) w on (to_char(to_date(substr(t.LOGDATE,1,10), 'DD-MM-YY'),'IW') = w.week)
order by w.week)
where trunc(to_date(substr(LOGDATE,1,10), 'DD-MM-YY')) BETWEEN '01-JAN-14' AND '31-DEC-14'
group by nvl(to_char(NEXT_DAY(to_date(substr(LOGDATE,1,10), 'DD-MM-YY') ,'SATURDAY'), 'IW'),week)
;
--Creating a test table under schema alber and inserting data on it for all days on weeks multiple of 4.
create table alber.week_table (mydate date, mydescription varchar2(100));
declare
cur_date date := to_date('01/01/'||to_char(sysdate,'yyyy'),'dd/mm/yyyy') ;
week number := 01;
begin
EXECUTE IMMEDIATE 'TRUNCATE TABLE alber.week_table';
while (cur_date < sysdate) loop
if mod(week,4) = 0 then
insert into alber.week_table values(cur_date, to_char(sysdate, 'day dd month yyyy hh24:mi:ss')) ;
end if;
cur_date := cur_date + 1;
week := to_number(to_char(cur_date,'IW'));
end loop;
end;
--Then one possible solution would be:
select theweek "Week", sum( decode( nvl(mydescription,0),'0',0,1) ) "Num of Records"
FROM
(
SELECT to_char(SYSDATE - (LEVEL * 7),'IW') theweek
FROM dual
CONNECT BY LEVEL <= 52
) w
LEFT JOIN week_table ON w.theweek = to_char(mydate,'IW')
group by theweek
order by 1;
Result
Week Num of Records
---- ----------------------
01 0
02 0
03 0
04 7
05 0
06 0
07 0
08 7
09 0
10 0
11 0
12 7
13 0
14 0
15 0
16 7
17 0
18 0
.
.
.