How to get the sum of data from different dates - sql

I need to get 3 data
The sum of data between n dates / days
like, I need to get the sum of 01-12 to 05-12 and this / days = 5
The result of 1 but this is for validation
The difference between #1 and #2
How to get the sum of data from different dates also I need this 3 points for every segment I have like
segment
1
2
3
TTT
456465
456465
0
CCC
478888
478886
2
select segment,
(SELECT var1 AS 1.-
+ ( SELECT var1 AS 1.-
from table
where data = 20221207
group by segement
)
from table
where data = 20221206
group by segement) AS 1.-,
sum(IMP_SDO_MED_CONT_ML) AS 2.-,
(1.- - 2.-) AS difference,
from table
WHERE DATA = 20221207
group by segment;

You appear to want to sum the data using conditional aggregation and then can find #3 using subtraction:
SELECT segment,
SUM(CASE WHEN data BETWEEN 20221201 AND 20221212 THEN var1 END) AS "1",
SUM(IMP_SDO_MED_CONT_ML) AS "2",
SUM(CASE WHEN data BETWEEN 20221201 AND 20221212 THEN var1 END)
- SUM(IMP_SDO_MED_CONT_ML) AS "3"
FROM table_name
GROUP BY segment
However, without sample input data it is difficult to check what you are expecting.

It's realy hard to tell what the question is without the data. Maybe it is about calculating the difference of sums of two columns in some period per segments. If that is the case then the query would be:
Select
SEGMENT,
Sum(var_1) "FLD_1",
Sum(IMP_SDO_MED_CONT_ML) "FLD_2",
Sum(var_1) - Sum(IMP_SDO_MED_CONT_ML) "DIFF"
From a_tbl
Where DATE_NUMBER Between 20221201 And 20221203
Group By SEGMENT
Order By SEGMENT
... and if we invent some sample data like below
WITH
a_tbl AS
(
Select 'AAA' "SEGMENT", 1234 "VAR_1", 1233 "IMP_SDO_MED_CONT_ML", 20221201 "DATE_NUMBER" From Dual Union All
Select 'AAA' "SEGMENT", 5678 "VAR_1", 5677 "IMP_SDO_MED_CONT_ML", 20221202 "DATE_NUMBER" From Dual Union All
Select 'AAA' "SEGMENT", 9101 "VAR_1", 9103 "IMP_SDO_MED_CONT_ML", 20221203 "DATE_NUMBER" From Dual Union All
Select 'BBB' "SEGMENT", 8765 "VAR_1", 8766 "IMP_SDO_MED_CONT_ML", 20221201 "DATE_NUMBER" From Dual Union All
Select 'BBB' "SEGMENT", 6666 "VAR_1", 6665 "IMP_SDO_MED_CONT_ML", 20221202 "DATE_NUMBER" From Dual Union All
Select 'BBB' "SEGMENT", 4423 "VAR_1", 4420 "IMP_SDO_MED_CONT_ML", 20221203 "DATE_NUMBER" From Dual Union All
Select 'CCC' "SEGMENT", 1234 "VAR_1", 1233 "IMP_SDO_MED_CONT_ML", 20221201 "DATE_NUMBER" From Dual Union All
Select 'CCC' "SEGMENT", 5678 "VAR_1", 5677 "IMP_SDO_MED_CONT_ML", 20221203 "DATE_NUMBER" From Dual Union All
Select 'DDD' "SEGMENT", 1234 "VAR_1", 1233 "IMP_SDO_MED_CONT_ML", 20221201 "DATE_NUMBER" From Dual Union All
Select 'EEE' "SEGMENT", 5678 "VAR_1", 5678 "IMP_SDO_MED_CONT_ML", 20221203 "DATE_NUMBER" From Dual
)
... then the result would be
SEGMENT
FLD_1
FLD_2
DIFF
AAA
16013
16013
0
BBB
19854
19851
3
CCC
6912
6910
2
DDD
1234
1233
1
EEE
5678
5678
0
OR maybe it is the difference between sum of the period for one column and sum of the last day of period for the other column. In that case query could be:
Select
SEGMENT,
Sum(var_1) "FLD_1",
Sum(CASE WHEN DATE_NUMBER = 20221203 THEN IMP_SDO_MED_CONT_ML ELSE 0 END) "FLD_2",
Sum(var_1) - Sum(CASE WHEN DATE_NUMBER = 20221203 THEN IMP_SDO_MED_CONT_ML ELSE 0 END) "DIFF"
From a_tbl
Where DATE_NUMBER Between 20221201 And 20221203
Group By SEGMENT
Order By SEGMENT
... resulting (with same invented data) as
SEGMENT
FLD_1
FLD_2
DIFF
AAA
16013
9103
6910
BBB
19854
4420
15434
CCC
6912
5677
1235
DDD
1234
0
1234
EEE
5678
5678
0

Related

Is there a way to group values getting only the ones that verifies certain condition?

I'm trying to write a query in Oracle SQL that aggregates values by some ids, where I have the following table as an input:
ID
SOME_DATE
RANK_POSITION
301
20211201
1
301
20211202
2
301
20211203
3
649
20211201
1
649
20211202
2
649
20211206
3
649
20211208
4
649
20211211
5
758
20211212
1
758
20211222
2
And y want to obtain something like this:
ID
FIRST_IN_RANK_DATE
SECOND_IN_RANK_DATE
301
01/12/2021
02/12/2021
649
01/12/2021
02/12/2021
758
12/12/2021
22/12/2021
Where FIRST_IN_RANK_DATE, is the date that is the first in the RANK_POSITION for the ID, and SECOND_IN_RANK_DATE is the date that is second in RANK_POSITION for the specific ID.
You can use conditional aggregation:
SELECT id,
MAX(CASE rank_position WHEN 1 THEN some_date END) AS first_in_rank_date,
MAX(CASE rank_position WHEN 2 THEN some_date END) AS second_in_rank_date
FROM table_name
GROUP BY id
Or PIVOT:
SELECT *
FROM table_name
PIVOT (
MAX(some_date)
FOR rank_position IN (
1 AS first_in_rank_date,
2 AS second_in_rank_date
)
)
Or, from Oracle 12, MATCH_RECOGNIZE:
SELECT *
FROM table_name
MATCH_RECOGNIZE (
PARTITION BY id
ORDER BY rank_position
MEASURES
rank1.some_date AS first_in_rank_date,
rank2.some_date AS second_in_rank_date
PATTERN ( ^ rank1 rank2 )
DEFINE
rank1 AS rank_position = 1,
rank2 AS rank_position = 2
)
Which, for the sample data:
CREATE TABLE table_name (ID, SOME_DATE, RANK_POSITION) AS
SELECT 301, DATE '2021-12-01', 1 FROM DUAL UNION ALL
SELECT 301, DATE '2021-12-02', 2 FROM DUAL UNION ALL
SELECT 301, DATE '2021-12-03', 3 FROM DUAL UNION ALL
SELECT 649, DATE '2021-12-01', 1 FROM DUAL UNION ALL
SELECT 649, DATE '2021-12-02', 2 FROM DUAL UNION ALL
SELECT 649, DATE '2021-12-06', 3 FROM DUAL UNION ALL
SELECT 649, DATE '2021-12-08', 4 FROM DUAL UNION ALL
SELECT 649, DATE '2021-12-11', 5 FROM DUAL UNION ALL
SELECT 758, DATE '2021-12-12', 1 FROM DUAL UNION ALL
SELECT 758, DATE '2021-12-22', 2 FROM DUAL;
All output:
ID
FIRST_IN_RANK_DATE
SECOND_IN_RANK_DATE
301
2021-12-01 00:00:00
2021-12-02 00:00:00
649
2021-12-01 00:00:00
2021-12-02 00:00:00
758
2021-12-12 00:00:00
2021-12-22 00:00:00
db<>fiddle here

SQL - Join with window

I have this dataset of product sales:
PRODUCT SALES
YearMonth Client_ID
202103 1
202008 1
201807 1
202101 2
202011 2
201802 2
201801 2
And I have this dataset of Financial Sales
FINANCIAL SALES
YearMonth Client_ID
202104 1
202009 1
201607 1
202104 2
202012 2
201512 2
I want to create a column in the dataset PRODUCT SALES named "If_financial_sales" where it takes the value 1 when the client in product sales bougth a financial product in the last 24 months, and 0 otherwise. Those 24 months are counting from the YearMonth of PRODUCT SALES (not from today). For example, If the client 2 on 201802 from product sales date bought a financial sale in the last 24 months (from 201801 to 201601 = 24 months) then If_financial_sales = 1 for this client in that month.
Output expected:
PRODUCT SALES
YearMonth Client_ID If_Financial_Sales
202103 1 1
202008 1 0
201807 1 1
202101 2 1
202011 2 0
201802 2 0
201801 2 1
I have been trying this with ROW_NUMBER(), but it seems to work better with some joins I have seen that uses equality operatos on the keys when joining (>, <, =) but I don't know what their name is neither how to use them.
Try this:
-- Auxiliary data for testing purposes
with product_sales (YearMonth, Client_ID) as (
select 202103, 1 from dual union all
select 202008, 1 from dual union all
select 201807, 1 from dual union all
select 202101, 2 from dual union all
select 202011, 2 from dual union all
select 201802, 2 from dual union all
select 201801, 2 from dual),
financial_sales (YearMonth, Client_ID) as (
select 202104, 1 from dual union all
select 202009, 1 from dual union all
select 201607, 1 from dual union all
select 202104, 2 from dual union all
select 202012, 2 from dual union all
select 201512, 2 from dual)
-- Actual query
SELECT
ps.*,
CASE
WHEN EXISTS (
SELECT
1
FROM
financial_sales fs
WHERE
to_date(fs.yearmonth, 'YYYYMM') < to_date(ps.yearmonth, 'YYYYMM')
AND to_date(fs.yearmonth, 'YYYYMM') >= to_date(ps.yearmonth, 'YYYYMM') - INTERVAL '25' MONTH
AND fs.client_id = ps.client_id
) THEN
1
ELSE
0
END if_financial_sales
FROM
product_sales ps;
Output:
YearMonth Client_ID If_financial_sales
----------------------
202103 1 1
202008 1 0
201807 1 1
202101 2 1
202011 2 0
201802 2 0
201801 2 1

SQL select lapsed customers with 30 day frequency by day

The goal is to select the count of distinct customer_id's who have not made a purchase in the rolling 30 day period prior to every day in the calendar year 2016. I have created a calendar table in my database to join to.
Here is an example table for reference, let's say you have customers orders normalized as follows:
+-------------+------------+----------+
| customer_id | date | order_id |
+-------------+------------+----------+
| 123 | 01/25/2016 | 1000 |
+-------------+------------+----------+
| 123 | 04/27/2016 | 1025 |
+-------------+------------+----------+
| 444 | 02/02/2016 | 1010 |
+-------------+------------+----------+
| 521 | 01/23/2016 | 998 |
+-------------+------------+----------+
| 521 | 01/24/2016 | 999 |
+-------------+------------+----------+
The goal output is effectively a calendar with 1 row for every single day of 2016 with a count on each day of how many customers "lapsed" on that day, meaning their last purchase was 30 days or more prior from that day of the year. The final output will look like this:
+------------+--------------+
| date | lapsed_count |
+------------+--------------+
| 01/01/2016 | 0 |
+------------+--------------+
| 01/02/2016 | 0 |
+------------+--------------+
| ... | ... |
+------------+--------------+
| 03/01/2016 | 12 |
+------------+--------------+
| 03/02/2016 | 9 |
+------------+--------------+
| 03/03/2016 | 7 |
+------------+--------------+
This data does not exist in 2015, therefore it's not possible for Jan-01-2016 to have a count of lapsed customers because that is the first possible day to ever make a purchase.
So for customer_id #123, they purchased on 01/25/2016 and 04/27/2016. They should have 2 lapse counts because their purchases are more than 30 days apart. One lapse occurring on 2/24/2016 and another lapse on 05/27/2016.
Customer_id#444 only purchased once, so they should have one lapse count for 30 days after 02/02/2016 on 03/02/2016.
Customer_id#521 is tricky, since they purchased with a frequency of 1 day we will not count the first purchase on 03/02/2016, so there is only one lapse starting from their last purchase of 03/03/2016. The count for the lapse will occur on 04/02/2016 (+30 days).
If you have a table of dates, here is one expensive method:
select date,
sum(case when prev_date < date - 30 then 1 else 0 end) as lapsed
from (select c.date, o.customer_id, max(o.date) as prev_date
from calendar c cross join
(select distinct customer_id from orders) c left join
orders o
on o.date <= c.date and o.customer_id = c.customer_id
group by c.date, o.customer_id
) oc
group by date;
For each date/customer pair, it determines the latest purchase the customer made before the date. It then uses this information to count the lapsed.
To be honest, this will probably work well on a handful of dates, but not for a full year's worth.
Apologies, I didn't read your question properly the first time around. This query will give you all the lapses you have. It takes each order and uses an analytic function to work out the next order date - if the gap is greater than 30 days then a lapse is recorded
WITH
cust_orders (customer_id , order_date , order_id )
AS
(SELECT 1, TO_DATE('01/01/2016','DD/MM/YYYY'), 1001 FROM dual UNION ALL
SELECT 1, TO_DATE('29/01/2016','DD/MM/YYYY'), 1002 FROM dual UNION ALL
SELECT 1, TO_DATE('01/03/2016','DD/MM/YYYY'), 1003 FROM dual UNION ALL
SELECT 2, TO_DATE('01/01/2016','DD/MM/YYYY'), 1004 FROM dual UNION ALL
SELECT 2, TO_DATE('29/01/2016','DD/MM/YYYY'), 1005 FROM dual UNION ALL
SELECT 2, TO_DATE('01/04/2016','DD/MM/YYYY'), 1006 FROM dual UNION ALL
SELECT 2, TO_DATE('01/06/2016','DD/MM/YYYY'), 1007 FROM dual UNION ALL
SELECT 2, TO_DATE('01/08/2016','DD/MM/YYYY'), 1008 FROM dual UNION ALL
SELECT 3, TO_DATE('01/09/2016','DD/MM/YYYY'), 1009 FROM dual UNION ALL
SELECT 3, TO_DATE('01/12/2016','DD/MM/YYYY'), 1010 FROM dual UNION ALL
SELECT 3, TO_DATE('02/12/2016','DD/MM/YYYY'), 1011 FROM dual UNION ALL
SELECT 3, TO_DATE('03/12/2016','DD/MM/YYYY'), 1012 FROM dual UNION ALL
SELECT 3, TO_DATE('04/12/2016','DD/MM/YYYY'), 1013 FROM dual UNION ALL
SELECT 3, TO_DATE('05/12/2016','DD/MM/YYYY'), 1014 FROM dual UNION ALL
SELECT 3, TO_DATE('06/12/2016','DD/MM/YYYY'), 1015 FROM dual UNION ALL
SELECT 3, TO_DATE('07/12/2016','DD/MM/YYYY'), 1016 FROM dual
)
SELECT
customer_id
,order_date
,order_id
,next_order_date
,order_date + 30 lapse_date
FROM
(SELECT
customer_id
,order_date
,order_id
,LEAD(order_date) OVER (PARTITION BY customer_id ORDER BY order_date) next_order_date
FROM
cust_orders
)
WHERE NVL(next_order_date,sysdate) - order_date > 30
;
Now join that to a set of dates and run a COUNT function (enter the year parameter as YYYY) :
WITH
cust_orders (customer_id , order_date , order_id )
AS
(SELECT 1, TO_DATE('01/01/2016','DD/MM/YYYY'), 1001 FROM dual UNION ALL
SELECT 1, TO_DATE('29/01/2016','DD/MM/YYYY'), 1002 FROM dual UNION ALL
SELECT 1, TO_DATE('01/03/2016','DD/MM/YYYY'), 1003 FROM dual UNION ALL
SELECT 2, TO_DATE('01/01/2016','DD/MM/YYYY'), 1004 FROM dual UNION ALL
SELECT 2, TO_DATE('29/01/2016','DD/MM/YYYY'), 1005 FROM dual UNION ALL
SELECT 2, TO_DATE('01/04/2016','DD/MM/YYYY'), 1006 FROM dual UNION ALL
SELECT 2, TO_DATE('01/06/2016','DD/MM/YYYY'), 1007 FROM dual UNION ALL
SELECT 2, TO_DATE('01/08/2016','DD/MM/YYYY'), 1008 FROM dual UNION ALL
SELECT 3, TO_DATE('01/09/2016','DD/MM/YYYY'), 1009 FROM dual UNION ALL
SELECT 3, TO_DATE('01/12/2016','DD/MM/YYYY'), 1010 FROM dual UNION ALL
SELECT 3, TO_DATE('02/12/2016','DD/MM/YYYY'), 1011 FROM dual UNION ALL
SELECT 3, TO_DATE('03/12/2016','DD/MM/YYYY'), 1012 FROM dual UNION ALL
SELECT 3, TO_DATE('04/12/2016','DD/MM/YYYY'), 1013 FROM dual UNION ALL
SELECT 3, TO_DATE('05/12/2016','DD/MM/YYYY'), 1014 FROM dual UNION ALL
SELECT 3, TO_DATE('06/12/2016','DD/MM/YYYY'), 1015 FROM dual UNION ALL
SELECT 3, TO_DATE('07/12/2016','DD/MM/YYYY'), 1016 FROM dual
)
,calendar (date_value)
AS
(SELECT TO_DATE('01/01/'||:P_year,'DD/MM/YYYY') + (rownum -1)
FROM all_tables
WHERE rownum < (TO_DATE('31/12/'||:P_year,'DD/MM/YYYY') - TO_DATE('01/01/'||:P_year,'DD/MM/YYYY')) + 2
)
SELECT
calendar.date_value
,COUNT(*)
FROM
(
SELECT
customer_id
,order_date
,order_id
,next_order_date
,order_date + 30 lapse_date
FROM
(SELECT
customer_id
,order_date
,order_id
,LEAD(order_date) OVER (PARTITION BY customer_id ORDER BY order_date) next_order_date
FROM
cust_orders
)
WHERE NVL(next_order_date,sysdate) - order_date > 30
) lapses
,calendar
WHERE 1=1
AND calendar.date_value = TRUNC(lapses.lapse_date)
GROUP BY
calendar.date_value
;
Or if you really want every date printed out then use this :
WITH
cust_orders (customer_id , order_date , order_id )
AS
(SELECT 1, TO_DATE('01/01/2016','DD/MM/YYYY'), 1001 FROM dual UNION ALL
SELECT 1, TO_DATE('29/01/2016','DD/MM/YYYY'), 1002 FROM dual UNION ALL
SELECT 1, TO_DATE('01/03/2016','DD/MM/YYYY'), 1003 FROM dual UNION ALL
SELECT 2, TO_DATE('01/01/2016','DD/MM/YYYY'), 1004 FROM dual UNION ALL
SELECT 2, TO_DATE('29/01/2016','DD/MM/YYYY'), 1005 FROM dual UNION ALL
SELECT 2, TO_DATE('01/04/2016','DD/MM/YYYY'), 1006 FROM dual UNION ALL
SELECT 2, TO_DATE('01/06/2016','DD/MM/YYYY'), 1007 FROM dual UNION ALL
SELECT 2, TO_DATE('01/08/2016','DD/MM/YYYY'), 1008 FROM dual UNION ALL
SELECT 3, TO_DATE('01/09/2016','DD/MM/YYYY'), 1009 FROM dual UNION ALL
SELECT 3, TO_DATE('01/12/2016','DD/MM/YYYY'), 1010 FROM dual UNION ALL
SELECT 3, TO_DATE('02/12/2016','DD/MM/YYYY'), 1011 FROM dual UNION ALL
SELECT 3, TO_DATE('03/12/2016','DD/MM/YYYY'), 1012 FROM dual UNION ALL
SELECT 3, TO_DATE('04/12/2016','DD/MM/YYYY'), 1013 FROM dual UNION ALL
SELECT 3, TO_DATE('05/12/2016','DD/MM/YYYY'), 1014 FROM dual UNION ALL
SELECT 3, TO_DATE('06/12/2016','DD/MM/YYYY'), 1015 FROM dual UNION ALL
SELECT 3, TO_DATE('07/12/2016','DD/MM/YYYY'), 1016 FROM dual
)
,lapses
AS
(SELECT
customer_id
,order_date
,order_id
,next_order_date
,order_date + 30 lapse_date
FROM
(SELECT
customer_id
,order_date
,order_id
,LEAD(order_date) OVER (PARTITION BY customer_id ORDER BY order_date) next_order_date
FROM
cust_orders
)
WHERE NVL(next_order_date,sysdate) - order_date > 30
)
,calendar (date_value)
AS
(SELECT TO_DATE('01/01/'||:P_year,'DD/MM/YYYY') + (rownum -1)
FROM all_tables
WHERE rownum < (TO_DATE('31/12/'||:P_year,'DD/MM/YYYY') - TO_DATE('01/01/'||:P_year,'DD/MM/YYYY')) + 2
)
SELECT
calendar.date_value
,(SELECT COUNT(*)
FROM lapses
WHERE calendar.date_value = lapses.lapse_date
)
FROM
calendar
WHERE 1=1
ORDER BY
calendar.date_value
;
Here's how I'd do it:
WITH your_table AS (SELECT 123 customer_id, to_date('24/01/2016', 'dd/mm/yyyy') order_date, 12345 order_id FROM dual UNION ALL
SELECT 123 customer_id, to_date('24/01/2016', 'dd/mm/yyyy') order_date, 12346 order_id FROM dual UNION ALL
SELECT 123 customer_id, to_date('25/01/2016', 'dd/mm/yyyy') order_date, 12347 order_id FROM dual UNION ALL
SELECT 123 customer_id, to_date('24/02/2016', 'dd/mm/yyyy') order_date, 12347 order_id FROM dual UNION ALL
SELECT 123 customer_id, to_date('16/03/2016', 'dd/mm/yyyy') order_date, 12348 order_id FROM dual UNION ALL
SELECT 123 customer_id, to_date('18/04/2016', 'dd/mm/yyyy') order_date, 12349 order_id FROM dual UNION ALL
SELECT 456 customer_id, to_date('20/02/2016', 'dd/mm/yyyy') order_date, 12350 order_id FROM dual UNION ALL
SELECT 456 customer_id, to_date('01/03/2016', 'dd/mm/yyyy') order_date, 12351 order_id FROM dual UNION ALL
SELECT 456 customer_id, to_date('03/03/2016', 'dd/mm/yyyy') order_date, 12352 order_id FROM dual UNION ALL
SELECT 456 customer_id, to_date('18/04/2016', 'dd/mm/yyyy') order_date, 12353 order_id FROM dual UNION ALL
SELECT 456 customer_id, to_date('20/05/2016', 'dd/mm/yyyy') order_date, 12354 order_id FROM dual UNION ALL
SELECT 456 customer_id, to_date('23/06/2016', 'dd/mm/yyyy') order_date, 12355 order_id FROM dual UNION ALL
SELECT 456 customer_id, to_date('19/01/2017', 'dd/mm/yyyy') order_date, 12356 order_id FROM dual),
-- end of mimicking your_table with data in it
lapsed_info AS (SELECT customer_id,
order_date,
CASE WHEN TRUNC(SYSDATE) - order_date <= 30 THEN NULL
WHEN COUNT(*) OVER (PARTITION BY customer_id ORDER BY order_date RANGE BETWEEN 1 FOLLOWING AND 30 FOLLOWING) = 0 THEN order_date+30
ELSE NULL
END lapsed_date
FROM your_table),
dates AS (SELECT to_date('01/01/2016', 'dd/mm/yyyy') + LEVEL -1 dt
FROM dual
CONNECT BY to_date('01/01/2016', 'dd/mm/yyyy') + LEVEL -1 <= TRUNC(SYSDATE))
SELECT dates.dt,
COUNT(li.lapsed_date) lapsed_count
FROM dates
LEFT OUTER JOIN lapsed_info li ON dates.dt = li.lapsed_date
GROUP BY dates.dt
ORDER BY dates.dt;
Results:
DT LAPSED_COUNT
---------- ------------
01/01/2016 0
<snip>
23/01/2016 0
24/01/2016 0
25/01/2016 0
26/01/2016 0
<snip>
19/02/2016 0
20/02/2016 0
21/02/2016 0
22/02/2016 0
23/02/2016 0
24/02/2016 1
25/02/2016 0
<snip>
29/02/2016 0
01/03/2016 0
02/03/2016 0
03/03/2016 0
04/03/2016 0
<snip>
15/03/2016 0
16/03/2016 0
17/03/2016 0
<snip>
20/03/2016 0
21/03/2016 0
22/03/2016 0
<snip>
30/03/2016 0
31/03/2016 0
01/04/2016 0
02/04/2016 1
03/04/2016 0
<snip>
14/04/2016 0
15/04/2016 1
16/04/2016 0
17/04/2016 0
18/04/2016 0
19/04/2016 0
<snip>
17/05/2016 0
18/05/2016 2
19/05/2016 0
20/05/2016 0
21/05/2016 0
<snip>
18/06/2016 0
19/06/2016 1
20/06/2016 0
21/06/2016 0
22/06/2016 0
23/06/2016 0
24/06/2016 0
<snip>
22/07/2016 0
23/07/2016 1
24/07/2016 0
<snip>
18/01/2017 0
19/01/2017 0
20/01/2017 0
<snip>
08/02/2017 0
This takes your data, and uses an the analytic count function to work out the number of rows that have a value within 30 days of (but excluding) the current row's date.
Then we apply a case expression to determine that if the row has a date within 30 days of today's date, we'll count those as not lapsed. If a count of 0 was returned, then the row is considered lapsed and we'll output the lapsed date as the order_date plus 30 days. Any other count result means the row has not lapsed.
The above is all worked out in the lapsed_info subquery.
Then all we need to do is list the dates (see the dates subquery) and outer join the lapsed_info subquery to it based on the lapsed_date and then do a count of the lapsed dates for each day.

Oracle - Assigning the correct Date from a Set

I have a table A like below
REGID | PKG_DESC | EVENT_DATE | IS_CON | IS_REN
-----------------------------------------------------
1234 | cc | 27-MAR-14 | 0 | 0
1234 | cc | 27-JUN-14 | 1 | 0
1234 | GUI | 27-MAR-14 | 0 | 0
1234 | GUI | 27-JUN-14 | 1 | 0
1234 | GUI | 27-SEPT-14 | 0 | 1
1234 | GUI | 27-SEPT-15 | 0 | 1
1234 | REMOTE | 27-MAR-14 | 0 | 0
1234 | REMOTE | 27-JUN-14 | 1 | 0
1234 | REMOTE | 27-SEPT-14 | 0 | 1
2431 | cc | 27-MAR-14 | 0 | 0
2431 | cc | 27-JUN-14 | 1 | 0
I have a query like below
select a.reg_id, b.sess_start_dt,
case when TRUNC(A.EVENT_DATE) - B.SESS_START_DT BETWEEN 0-30 THEN 'DAYS 0_30'
WHEN TRUNC(A.EVENT_DATE) - B.SESS_START_DT BETWEEN 31-60 THEN 'DAYS 31-60'
from tab a inner join tab b on a.reg_id = b.reg_id and a.is_ren = 1
union
select a.reg_id, b.sess_start_dt,
case when TRUNC(A.EVENT_DATE) - B.SESS_START_DT BETWEEN 0-30 THEN 'DAYS 0_30'
WHEN TRUNC(A.EVENT_DATE) - B.SESS_START_DT BETWEEN 31-60 THEN 'DAYS 31-60'
from tab a inner join tab b on a.reg_id = b.reg_id and a.is_con = 1
Tab B contains all the usage for each reg_id there will be 100's of records.. Sample of few are
REGID | SESS_START_DT
1234 | 27-Jan-14
1234 | 20-MAR-12
1234 | 27-MAR-12
1234 | 01-sept-14
1234 | 07-sept-14
1234 | 29-JUL-14
1234 | 03-AUG-14
1234 | 27-MAR-13
1234 | 27-MAR-12
1234 | 27-MAR-12
1234 | 27-MAR-12
1234 | 27-MAR-12
1234 | 27-MAR-12
1234 | 27-MAR-12
2431 | 20-JUN-14
The Above query needs to be corrected in a way like,
1) If the REG_ID is having at least one is_ren = 1 then that subscription should be considered as renewal subscription and needs to get the 30 days and 60 days usage from table B from his is_ren = 1 event_date. (for REGID 1234 only is_ren query should execute)
2) If multiple IS_REN = 1 are existing for each REGID then the usage needs to be taken 30 days and 60 days from table B with the MIN(event_date). in this case the usage should be taken from 27-SEPT-14 instead of 27-SEPT-15
3) If there is no IS_REN = 1 and there is IS_CON = 1 then it's considered as conversion and usage should be taken before 60 days from the converted date (for REGID 2431, usage needs to get 60 days back from 27-JUN-14{this is my event_date in the query})
The O/P should be like
REGID | EVENT_DATE | DAYS 0_30 | DAYS 31-60 | CODE
1234 | 27-SEPT-14 | 2 | 2 | REN
2431 | 27-JUL-14 | 1 | 0 | CON
If my assumptions in my Comment are correct, this may be what you need. Notice the order by clause in row_number() - first the rows with is_ren = 1, then the rows with is_ren = 0 and is_con = 1, then all the other rows, and within each group order by event_date ascending. This way, the top row (rn = 1), which is the only one I use in the outer query, will have is_ren = 1 with the earliest possible date, or if no is_ren = 1 then the row with is_con = 1 and the earliest date, or else just the earliest date. (In the last case, the CODE will be null: this means there were no is_ren = 1 and no is_con = 1 for that regid.
Not sure why you have 27-JUL-14 in the output for regid = 2431, that should be 27-JUN-14. Also, there are no four-letter months in Oracle ("SEPT"). The output shows dates using my session parameters; if you need to format the dates, use to_date(event_date, .....) with the desired date format model. Also, since the data you provided is just dates (with no time-of-day component), I didn't truncate anything; you may need to, if your real data has time-of-day components.
with
table_a ( regid, pkg_desc, event_date, is_con, is_ren ) as (
select 1234, 'cc' , to_date ('27-MAR-14', 'dd-MON-rr'), 0, 0 from dual union all
select 1234, 'cc' , to_date ('27-JUN-14', 'dd-MON-rr'), 1, 0 from dual union all
select 1234, 'GUI' , to_date ('27-MAR-14', 'dd-MON-rr'), 0, 0 from dual union all
select 1234, 'GUI' , to_date ('27-JUN-14', 'dd-MON-rr'), 1, 0 from dual union all
select 1234, 'GUI' , to_date ('27-SEP-14', 'dd-MON-rr'), 0, 1 from dual union all
select 1234, 'GUI' , to_date ('27-SEP-15', 'dd-MON-rr'), 0, 1 from dual union all
select 1234, 'REMOTE', to_date ('27-MAR-14', 'dd-MON-rr'), 0, 0 from dual union all
select 1234, 'REMOTE', to_date ('27-JUN-14', 'dd-MON-rr'), 1, 0 from dual union all
select 1234, 'REMOTE', to_date ('27-SEP-14', 'dd-MON-rr'), 0, 1 from dual union all
select 2431, 'cc' , to_date ('27-MAR-14', 'dd-MON-rr'), 0, 0 from dual union all
select 2431, 'cc' , to_date ('27-JUN-14', 'dd-MON-rr'), 1, 0 from dual
),
table_b ( regid, sess_start_dt ) as (
select 1234, to_date ('27-JAN-14', 'dd-MON-rr') from dual union all
select 1234, to_date ('20-MAR-12', 'dd-MON-rr') from dual union all
select 1234, to_date ('27-MAR-12', 'dd-MON-rr') from dual union all
select 1234, to_date ('01-SEP-14', 'dd-MON-rr') from dual union all
select 1234, to_date ('07-SEP-14', 'dd-MON-rr') from dual union all
select 1234, to_date ('29-JUL-14', 'dd-MON-rr') from dual union all
select 1234, to_date ('03-AUG-14', 'dd-MON-rr') from dual union all
select 1234, to_date ('27-MAR-13', 'dd-MON-rr') from dual union all
select 1234, to_date ('27-MAR-12', 'dd-MON-rr') from dual union all
select 1234, to_date ('27-MAR-12', 'dd-MON-rr') from dual union all
select 1234, to_date ('27-MAR-12', 'dd-MON-rr') from dual union all
select 1234, to_date ('27-MAR-12', 'dd-MON-rr') from dual union all
select 1234, to_date ('27-MAR-12', 'dd-MON-rr') from dual union all
select 1234, to_date ('27-MAR-12', 'dd-MON-rr') from dual union all
select 2431, to_date ('20-JUN-14', 'dd-MON-rr') from dual
),
prep ( regid, event_date, code, rn ) as (
select regid, event_date,
case when is_ren = 1 then 'REN' when is_con = 1 then 'CON' else null end,
row_number() over (partition by regid
order by case when is_ren = 1 then 0
when is_con = 1 then 1 else 2 end,
event_date)
from table_a
)
select p.regid, p.event_date,
count(case when b.sess_start_dt between p.event_date - 30 and p.event_date
then 1 end) as days_0_30,
count(case when b.sess_start_dt between p.event_date - 60 and p.event_date - 31
then 1 end) as days_31_60,
p.code
from prep p inner join table_b b on p.regid = b.regid
where rn = 1
group by p.regid, p.event_date, p.code
;
Output:
REGID EVENT_DATE DAYS_0_30 DAYS_31_60 COD
---------- ------------------- ---------- ---------- ---
1234 2014-09-27 00:00:00 2 2 REN
2431 2014-06-27 00:00:00 1 0 CON

How to group by dates into quarterly manner

Hi I have table with column ID and DATES I want to make another column which store dates group by quarterly.
For example
Table
ID DATES
123 5/1/2005
123 7/1/2001
123 4/1/2003
123 2/1/2002
123 6/1/2005
123 6/1/2004
expected output:
ID DATES QUATER
123 5/1/2005 Q2-2005
123 7/1/2001 Q3-2001
123 4/1/2003 Q2-2003
123 2/1/2002 Q1-2002
123 6/1/2005 Q2-2005
123 6/1/2004 Q2-2004
You can do this with to_char(), like so:
with sample_data as (select 123 id, to_date('05/01/2005', 'mm/dd/yyyy') dt from dual union all
select 123 id, to_date('07/01/2001', 'mm/dd/yyyy') dt from dual union all
select 123 id, to_date('04/01/2003', 'mm/dd/yyyy') dt from dual union all
select 123 id, to_date('02/01/2002', 'mm/dd/yyyy') dt from dual union all
select 123 id, to_date('06/01/2005', 'mm/dd/yyyy') dt from dual union all
select 123 id, to_date('06/01/2004', 'mm/dd/yyyy') dt from dual)
---- end of mimicking a table called sample_data that has your input data in it. See SQL below:
select id,
dt,
TO_CHAR(sd.dt, '"Q"q-yyyy') qtr
from sample_data sd;
ID DT QTR
---------- ---------- -------
123 05/01/2004 Q2-2005
123 07/01/2001 Q3-2001
123 04/01/2003 Q2-2003
123 02/01/2002 Q1-2002
123 06/01/2005 Q2-2005
123 06/01/2004 Q2-2004