ORACLE SQL - How to find number of purchases made before, during, and after birthday month? - sql

I would like to find the number of purchases made a month before & after a customer's birthday month.
For eg. Customer A's birthday is in June. I would like to know how many purchases he made in May, June and July.
I have 2 existing tables which look like the following:
PURCHASE HISTORY TABLE:
CUSTOMER_ID PURCHASE_DATE
1 2019-06-18
1 2019-05-23
1 2019-04-19
1 2019-04-01
2 2019-03-10
2 2019-02-28
2 2018-02-11
2 2018-02-02
Note: This table only contains transaction history. Dates without purchases are not recorded.
CUSTOMER DETAILS TABLE:
CUSTOMER_ID DATE_OF_BIRTH
1 1996-05-22
2 1990-03-07
How the output should look like:
CUSTOMER_ID BDAY_MONTH PURCHASE_B4_BDAY PURCHASE_BDAY PURCHASE_AFTER_BDAY
1 5 2 1 1
2 3 1 1 0
where PURCHASE_B4_BDAY = number of purchases made 1 month before birthday month
and PURCHASE_BDAY = number of purchases made during birthday month
and PURCHASE_AFTER_BDAY = number of purchases made 1 month after birthday month
Note: I only want to take into account the latest year of purchase.
For eg, customer id 2 has 1 purchases in Feb'19 and 2 purchases in Feb'18, however, only the ones made in Feb'19 should be considered.
Do let me know if you require more information, thanks a lot for your help!!!

You need to find the difference of the months between purchase and birth date and use MOD function to calculate if the purchase is made one month before, or on the exact month or one month after birth month as follows:
SELECT
CUSTOMER_ID,
EXTRACT(MONTH FROM DATE_OF_BIRTH),
SUM(CASE WHEN MONTHS_ = 11 THEN 1 END) AS PURCHASE_B4_BDAY,
SUM(CASE WHEN MONTHS_ = 0 THEN 1 END) AS PURCHASE_BDAY,
SUM(CASE WHEN MONTHS_ = 1 THEN 1 END) AS PURCHASE_AFTER_BDAY
FROM
(
SELECT
CD.CUSTOMER_ID,
MOD(MONTHS_BETWEEN(TRUNC(PH.PURCHASE_DATE, 'MONTH'),
TRUNC(CD.DATE_OF_BIRTH, 'MONTH')), 12) AS MONTHS_,
PH.PURCHASE_DATE,
CD.DATE_OF_BIRTH
FROM
PURCHASE_HISTORY PH
JOIN CUSTOMER_DETAILS CD ON PH.CUSTOMER_ID = CD.CUSTOMER_ID
)
WHERE MONTHS_ IN(1, 0, 11)
GROUP BY CD.CUSTOMER_ID;
Let me know if you find any difficulty in a given solution.
Cheers!!

Related

Find Individuals who have purchased 10 times within a rolling 1 year period

So let's say I have 2 tables. One table is for consumers, and another is for sales.
Consumers
ID
Name
...
1
John Johns
...
2
Cathy Dans
...
Sales
ID
consumer_id
purchase_date
...
1
1
01/03/05
...
2
1
02/04/10
...
3
1
03/04/11
...
4
2
02/14/07
...
5
2
09/24/08
...
6
2
12/15/09
...
I want to find all instances of consumers who made more than 10 purchases within any 6 month rolling period.
SELECT
consumers.id
, COUNT(sales.id)
FROM
consumers
JOIN sales ON consumers.id = sales.consumer_id
GROUP BY
consumers.id
HAVING
COUNT(sales.id) >= 10
ORDER BY
COUNT(sales.id) DESC
So I have this code, which just gives me a list of consumers who have made more than 10 purchases ALL TIME. But how do I incorporate the rolling 6 month period logic?!
Any help or guidance on which functions can help me accomplish this would be appreciated!
You can use window functions to count the number of sales in a six-month period. Then just filter down to those consumers:
select distinct consumer_id
from (select s.*,
count(*) over (partition by consumer_id
order by purchase_date
range between current row and interval '6 month' following
) as six_month_count
from sales s
) s
where six_month_count > 10;

SQL Troubleshooting Help on Table Structure

I'm attempting to calculate average number of days between a customer's 1st and 3rd purchase, but struggling to get the data ordered in a way that will allow me to calculate.
I currently have the below data table. (Note: Order sequence number refers to the number order for that customer.)
Order Date
Customer Number
Order Sequence Number
2020-09-20
1
1
2021-01-20
1
2
2021-01-21
1
3
2020-10-01
2
1
2020-08-06
3
1
2020-09-06
3
2
2020-09-09
3
3
I've been trying to get the data to look like the following table. [To then be able to calculate datediff on the last two columns.]
Customer Number
Order Count
First Order Date
Third Order Date
1
3
2020-09-20
2021-01-21
2
1
2020-10-01
Null
3
3
2020-08-06
2020-09-09
I've completely messed up the code, but here's what I've been trying.
CREATE TABLE X2 as
SELECT
customer_number,
max(order_sequence_number) as order_count,
CASE
WHEN order_sequence_number = 1 then order_date
ELSE null
END as first_order_date,
CASE
WHEN order_sequence_number = 3 then order_date
ELSE null
END as third_order_date
FROM X1
GROUP BY customer_number;
Can someone please tell me what I'm missing? Thanks in advance!
You are on the right track but you need aggregation functions:
SELECT customer_number,
max(order_sequence_number) as order_count,
MAX(CASE WHEN order_sequence_number = 1 THEN order_date END) as first_order_date,
MAX(CASE WHEN order_sequence_number = 3 THEN order_date END) as third_order_date
FROM X1
GROUP BY customer_number;
To get the difference in days, you would just subtract the two expressions using whatever date arithmetic is supported in your database.

how to fetch count data of 2 date fields in same month in SQL

I am trying to create a query where I have 3 column.
C_Time: contains task Creation date time
Done_Time: Contains Task completion date time
User ID: Unique id of user
I want to get result where I want to get total count of created tasks in particular month and total number of done task at that same month grouped by user id
Output will be like:
UserID | CreatedCount | DoneCount
------------------------------------------
U12 | 12 | 12
-------------------------------------------
U13 | 7 | 5
here U12 user have created 12 tasks and completed 12 tasks in January 2020 month. But user U13 created 7 tasks in Jan 2020 and done 5 tasks in same month.
You can use apply to unpivot the data and then aggregation:
select t.user_id, sum(is_create), sum(is_complete)
from t cross apply
(values (t.c_time, 1, 0), (t.done_time, 0, 1)
) v(t, is_create, is_complete)
where v.t >= '2020-01-01' and v.t < '2020-02-01'
group by t.user_id;
You can also do this with conditional aggregation:
select user_id,
sum(case when c_time >= '2020-01-01' and c_time < '2020-02-01' then 1 else 0 end),
sum(case when done_time >= '2020-01-01' and done_time < '2020-02-01' then 1 else 0 end)
from t
group by user_id;
This is probably a little faster for your particular example. However, the first version is more generalizable -- for instance, it allows you to summarize easily by both user and month.

finding the number of days in between first 2 date point

So the question seems to be quite difficult I wonder if I could get some advice from here. I am trying to solve this with SQLite 3. So I have a data format of this.
customer | purchase date
1 | date 1
1 | date 2
1 | date 3
2 | date 4
2 | date 5
2 | date 6
2 | date 7
number of times the customer repeats is random.
so I just want to find whether customer 1's 1st and 2nd purchase date are fallen in between a specific time period. repeat for other customers. only need to consider 1st and 2nd dates.
Any help would be appreciated!
We can try using ROW_NUMBER here:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY customer ORDER BY "purchase date") rn
FROM yourTable
)
SELECT
customer,
CAST(MAX(CASE WHEN rn = 2 THEN julianday("purchase date") END) -
MAX(CASE WHEN rn = 1 THEN julianday("purchase date") END) AS INTEGER) AS diff_in_days
FROM cte
GROUP BY
customer;
The idea here is to aggregate by customer and then take the date difference between the second and first purchase. ROW_NUMBER is used to find these first and second purchases, for each customer.

Splitting SQL Data Into Months

I have a Datatable with several hundred rows for this year in it. (MS SqlServer 2k8)
I would like to split this data set out into customer enquiries / Month.
What I have so far is;
Select count(id) As Customers, DatePart(month, enquiryDate) as MonthTotal, productCode From customerEnquiries
where enquiryDate > '2012-01-01 00:00:00'
group by productCode, enquiryDate
But this then produces a row for each data item. (Whereas I want a row per month for each data item.)
So how do I change the above query, so that instead of getting
1 1 10
1 1 10
1 1 11
1 2 10
1 2 10
...
I get
2 1 10 <-- 2 enquiries for product code 10 in month 1
1 1 11 <-- 1 enquiries for product code 11 in month 1
2 2 10 <-- 2 enquiries for product code 10 in month 2
etc
And as a bonus question, is there an easy way of naming each month so the output is Jan, Feb, March instead of 1,2,3 in the month column?
Try this
Select count(id) As Customers, DatePart(month, enquiryDate) as MonthTotal, productCode From customerEnquiries
where enquiryDate > '2012-01-01 00:00:00'
group by productCode, DatePart(month, enquiryDate)
This may help you.
For the Bonus, DATENAME(MONTH, enquiryDate) will give you the name of the Month.