a sql query to compare results of following queries - sql

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;

Related

How to count number of records for each week, from last month activity on a table?

I'm working with Oracle and I have a table with a column of type TIMESTAMP. I was wondering how can I extract the records from last 4 weeks of activity on the database, partitioned by week.
Following rows are inserted on week 1
kc 2 04-10-2021
vc 3 06-10-2021
vk 4 07-10-2021
Following rows are inserted on week2
cv 1 12-10-2021
ck 5 14-10-2021
Following rows are inserted on week3
vv 7 19-10-2021
Following rows are inserted on week4
vx 7 29-10-2021
Table now has
SQL>select * from tab;
NAME VALUE TIMESTAMP
-------------------- ----------
kc 2 04-10-2021
vc 3 06-10-2021
vk 4 07-10-2021
cv 1 12-10-2021
ck 5 14-10-2021
vv 7 19-10-2021
vx 7 29-10-2021
I would like a query which would give me the number of rows added each week, in the last 4 weeks.
This is what I would like to see
numofrows week
--------- -----
3 1
2 2
1 3
1 4
One option is to use to_char function and its iw parameter:
SQL> with test (name, datum) as
2 (select 'kc', date '2021-10-04' from dual union all
3 select 'vc', date '2021-10-06' from dual union all
4 select 'vk', date '2021-10-07' from dual union all
5 select 'cv', date '2021-10-12' from dual union all
6 select 'ck', date '2021-10-14' from dual union all
7 select 'vv', date '2021-10-19' from dual union all
8 select 'vx', DATE '2021-10-29' from dual
9 )
10 select to_char(datum, 'iw') week,
11 count(*)
12 from test
13 where datum >= add_months(sysdate, -1) --> the last month
14 group by to_char(datum, 'iw');
WE COUNT(*)
-- ----------
42 1
43 1
40 3
41 2
SQL>
Line #13: I intentionally used "one month" instead of "4 weeks" as I thought (maybe wrongly) that you, actually, want that (you know, "a month has 4 weeks" - not exactly, but close, sometimes not close enough).
If you want 4 weeks, what is that, then? Sysdate minus 28 days (as every week has 7 days)? Then you'd modify line #13 to
where datum >= trunc(sysdate - 4*7)
Or, maybe it is really the last 4 weeks:
SQL> with test (name, datum) as
2 (select 'kc', date '2021-10-04' from dual union all
3 select 'vc', date '2021-10-06' from dual union all
4 select 'vk', date '2021-10-07' from dual union all
5 select 'cv', date '2021-10-12' from dual union all
6 select 'ck', date '2021-10-14' from dual union all
7 select 'vv', date '2021-10-19' from dual union all
8 select 'vx', DATE '2021-10-29' from dual
9 ),
10 temp as
11 (select to_char(datum, 'iw') week,
12 count(*) cnt,
13 row_number() over (order by to_char(datum, 'iw') desc) rn
14 from test
15 group by to_char(datum, 'iw')
16 )
17 select week, cnt
18 from temp
19 where rn <= 4
20 order by week;
WE CNT
-- ----------
40 3
41 2
42 1
43 1
SQL>
Now you have several options, see which one fits the best (if any).
I "simulated" missing data (see TEST CTE), created a calendar (calend) and ... did the job. Read comments within code:
SQL> with test (name, datum) as
2 -- sample data
3 (select 'vv', date '2021-10-19' from dual union all
4 select 'vx', DATE '2021-10-29' from dual
5 ),
6 calend as
7 -- the last 31 days; 4 weeks are included, obviously
8 (select max_datum - level + 1 datum
9 from (select max(a.datum) max_datum from test a)
10 connect by level <= 31
11 ),
12 joined as
13 -- joined TEST and CALEND data
14 (select to_char(c.datum, 'iw') week,
15 t.name
16 from calend c left join test t on t.datum = c.datum
17 ),
18 last4 as
19 -- last 4 weeks
20 (select week, count(name) cnt,
21 row_number() over (order by week desc) rn
22 from joined
23 group by week
24 )
25 select week, cnt
26 from last4
27 where rn <= 4
28 order by week;
WE CNT
-- ----------
40 0
41 0
42 1
43 1
SQL>

How to loop in Oracle SQL

Assume we have exampleDB and select contractdate like
SELECT DB.contractdate
FROM exampleDB DB
contractdate
2014/12/1
2015/12/1
2016/12/1
2017/12/1
2018/12/1
2019/12/1
I would like to count the policy number at each time like
each time policy count
2014/1/1 0
2015/1/1 1
2016/1/1 2
2017/1/1 3
2018/1/1 4
2019/1/1 5
I tried
WHERE DB.contractdate <='2014/1/1';
But I must loop such code manually.
How can I loop?
If the binning is every month,it is very stressful process.
can they be combined into one?
Best regards
select contractdate as "each time",
count(*) as "policy count"
from exampleDB
where contractdate in (mention all dates you want)
group by contractdate
Hope this will help you.
you can use row_num() and trunc() to get 1st day of the month
SELECT TRUNC(DB.contractdate, 'MONTH'), row_number() over (order by DB.contractdate) -1 as policy_count
FROM exampleDB DB
You can use COUNT analytical function with RANGE operator as follows:
SQL> with dataa(contractdate) as
2 (
3 select date '2014-12-01' from dual union all
4 select date '2015-12-01' from dual union all
5 select date '2016-12-01' from dual union all
6 select date '2017-12-01' from dual union all
7 select date '2018-12-01' from dual union all
8 select date '2019-12-01' from dual
9 )
10 SELECT
11 TRUNC(CONTRACTDATE, 'year') as "each time",
12 COUNT(1) OVER(
13 ORDER BY
14 CONTRACTDATE DESC
15 RANGE BETWEEN 1 FOLLOWING AND UNBOUNDED FOLLOWING
16 ) as "policy count"
17 FROM
18 DATAA order by 1;
each time policy count
--------- ------------
01-JAN-14 0
01-JAN-15 1
01-JAN-16 2
01-JAN-17 3
01-JAN-18 4
01-JAN-19 5
6 rows selected.
SQL>
Cheers!!

sql oracle ignore holidays

I am using this code to calculate the difference between two dates ignoring weekends:
SELECT To_date(SYSDATE) -
To_date('01.07.2014', 'DD.MM.YYYY')
- 2 * ( TRUNC(Next_day(To_date(SYSDATE) - 1, 'FRI'))
- TRUNC( Next_day(To_date('01.07.2014' , 'DD.MM.YYYY')
- 1, 'FRI')) ) / 7 AS DAYS_BETWEEN
FROM dual
I have another table called table1 in which the column "date" exists (its type is "DATE") in which all dates where a holiday is are written down.
Example table 1:
DATES
12.06.2011
19.06.2014
09.05.2013
...
I am trying to make my code check this table and that if one date is between the two dates above it makes -1 day in the output.
It should be easy if you divide it into following tasks:
Generate all the dates between the two given dates using Row Generator method as shown here.
Ignore the dates which are weekend, i.e. Saturdays and Sundays
Check whether the dates in the range are having any match in the holiday table.
The following row generator query will give you the total count of weekdays, i.e. not including Saturdays and Sundays:
SQL> WITH dates 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(weekday) weekday_count
7 FROM
8 (SELECT
9 CASE
10 WHEN TO_CHAR(date1+LEVEL-1, 'DY','NLS_DATE_LANGUAGE=AMERICAN')
11 NOT IN ('SAT', 'SUN')
12 THEN 1
13 ELSE 0
14 END weekday
15 FROM dates
16 CONNECT BY LEVEL <= date2-date1+1
17 )
18 /
WEEKDAY_COUNT
-------------
261
SQL>
Now, based on above row generator query, let's see a test case.
The following query will calculate the count of working days between 1st Jan 2014 and 31st Dec 2014 excluding the holidays as mentioned in the table.
The WITH clause is only to use it as tables, in your case you can simply use your holiday table.
SQL> WITH dates
2 AS (SELECT To_date('01/01/2014', 'DD/MM/YYYY') date1,
3 To_date('31/12/2014', 'DD/MM/YYYY') date2
4 FROM dual),
5 holidays
6 AS (SELECT To_date('12.06.2011', 'DD.MM.YYYY') holiday FROM dual UNION ALL
7 SELECT To_date('19.06.2014', 'DD.MM.YYYY') holiday FROM dual UNION ALL
8 SELECT To_date('09.05.2013', 'DD.MM.YYYY') holiday FROM dual),
9 count_of_weekdays
10 AS (SELECT SUM(weekday) weekday_count
11 FROM (SELECT CASE
12 WHEN To_char(date1 + LEVEL - 1, 'DY',
13 'NLS_DATE_LANGUAGE=AMERICAN')
14 NOT IN (
15 'SAT',
16 'SUN' ) THEN 1
17 ELSE 0
18 END weekday
19 FROM dates
20 CONNECT BY LEVEL <= date2 - date1 + 1)),
21 count_of_holidays
22 AS (SELECT Count(*) holiday_count
23 FROM holidays
24 WHERE holiday NOT BETWEEN To_date('01/01/2015', 'DD/MM/YYYY') AND
25 To_date('31/03/2015', 'DD/MM/YYYY'))
26 SELECT weekday_count - holiday_count as working_day_count
27 FROM count_of_weekdays,
28 count_of_holidays
29 /
WORKING_DAY_COUNT
-----------------
258
SQL>
There were total 261 weekdays, out of which there were 3 holidays in holiday table. So, total count of working days in the output is 261 - 3 = 258.
SELECT To_date(sysdate)- To_date('01.07.2014','DD.MM.YYYY')
- (2 * (to_char(To_date(sysdate), 'WW') - to_char(To_date('01.07.2014','DD.MM.YYYY'), 'WW'))) AS DAYS_BETWEEN
FROM dual

How to finish this LAG calculation in Oracle

I have month and value columns in a table,like
Month Value Market
2010/01 100 1
2010/02 200 1
2010/03 300 1
2010/04 400 1
2010/05 500 1
2010/01 100 2
2010/02 200 2
2010/03 300 2
2010/04 400 2
2010/05 500 2
What I want to do is get new Month and Value combinations using (value in month(n-1)+value in month(n))/2=value in month n, also this calculation is based on market column, it group by market number. So, for the above example, the new month and value combination should be
Month Value Market
2010/01 null 1
2010/02 (100+200)/2 1
2010/03 (200+300)/2 1
2010/04 (300+400)/2 1
2010/05 (400+500)/2 1
2010/01 null 2
2010/02 (100+200)/2 2
2010/03 (200+300)/2 2
2010/04 (300+400)/2 2
2010/05 (400+500)/2 2
Do you know how to achieve it in Oracle? Thank you!
If there is no gap in your data, you can use LAG:
SQL> WITH DATA AS (
2 SELECT DATE '2010-01-01' mon, 100 val FROM dual UNION ALL
3 SELECT DATE '2010-02-01' mon, 200 val FROM dual UNION ALL
4 SELECT DATE '2010-03-01' mon, 300 val FROM dual UNION ALL
5 SELECT DATE '2010-04-01' mon, 400 val FROM dual UNION ALL
6 SELECT DATE '2010-05-01' mon, 500 val FROM dual
7 )
8 SELECT mon, (LAG(val) OVER (ORDER BY mon) + val) / 2 avg_val FROM DATA;
MON AVG_VAL
----------- ----------
01/01/2010
01/02/2010 150
01/03/2010 250
01/04/2010 350
01/05/2010 450
However, if there is a gap the result might not be what you expect. In that case, you can either use a self-join or narrow the windowing clause:
SQL> WITH DATA AS (
2 SELECT DATE '2010-01-01' mon, 100 val FROM dual UNION ALL
3 SELECT DATE '2010-02-01' mon, 200 val FROM dual UNION ALL
4 SELECT DATE '2010-03-01' mon, 300 val FROM dual UNION ALL
5 /* gap ! */
6 SELECT DATE '2010-05-01' mon, 400 val FROM dual UNION ALL
7 SELECT DATE '2010-06-01' mon, 500 val FROM dual
8 )
9 SELECT mon, (first_value(val)
10 OVER (ORDER BY mon
11 RANGE BETWEEN INTERVAL '1' MONTH PRECEDING
12 AND INTERVAL '1' MONTH PRECEDING)
13 + val) / 2 avg_val
14 FROM DATA;
MON AVG_VAL
----------- ----------
01/01/2010
01/02/2010 150
01/03/2010 250
01/05/2010
01/06/2010 450
This does it:
SQL> select month,
2 (value+lag(value) over (order by month))/2 as value
3* from t1
MONTH VALUE
---------- ----------
2010/01
2010/02 150
2010/03 250
2010/04 350
2010/05 450
5 rows selected.

oracle count based on month

I am attempting to write Oracle SQL.
I am looking for solution something similar. Please find below data I have
start_date end_date customer
01-01-2012 31-06-2012 a
01-01-2012 31-01-2012 b
01-02-2012 31-03-2012 c
I want the count of customer in that date period. My result should look like below
Month : Customer Count
JAN-12 : 2
FEB-12 : 2
MAR-12 : 2
APR-12 : 1
MAY-12 : 1
JUN-12 : 1
One option would be to generate the months separately in another query and join that to your data table (note that I'm assuming that you intended customer A to have an end-date of June 30, 2012 since there is no June 31).
SQL> ed
Wrote file afiedt.buf
1 with mnths as(
2 select add_months( date '2012-01-01', level - 1 ) mnth
3 from dual
4 connect by level <= 6 ),
5 data as (
6 select date '2012-01-01' start_date, date '2012-06-30' end_date, 'a' customer from dual union all
7 select date '2012-01-01', date '2012-01-31', 'b' from dual union all
8 select date '2012-02-01', date '2012-03-31', 'c' from dual
9 )
10 select mnths.mnth, count(*)
11 from data,
12 mnths
13 where mnths.mnth between data.start_date and data.end_date
14 group by mnths.mnth
15* order by mnths.mnth
SQL> /
MNTH COUNT(*)
--------- ----------
01-JAN-12 2
01-FEB-12 2
01-MAR-12 2
01-APR-12 1
01-MAY-12 1
01-JUN-12 1
6 rows selected.
WITH TMP(monthyear,start_date,end_date,customer) AS (
select LAST_DAY(start_date),
CAST(ADD_MONTHS(start_date, 1) AS DATE),
end_date,
customer
from data
union all
select LAST_DAY(start_date),
CAST(ADD_MONTHS(start_date, 1) AS DATE),
end_date,
customer
from TMP
where LAST_DAY(end_date) >= LAST_DAY(start_date)
)
SELECT TO_CHAR(MonthYear, 'MON-YY') TheMonth,
Count(Customer) Customers
FROM TMP
GROUP BY MonthYear
ORDER BY MonthYear;
SQLFiddle