Splitting SQL Data Into Months - sql

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.

Related

Query to find SUM based on week

I have a table like date , sales , region
date
Sales
Region
11/02/2021
20
1
12/02/2021
23
1
13/02/2021
30
2
14/02/2021
50
2
15/02/2021
10
3
16/02/2021
10
3
How to extract sum of sales per region based on weeks (Week starting from Monday to Sunday)
You need to select the week before grouping.
This should work for you:
SELECT DATEPART(week, date) AS Week,
FROM table
GROUP BY DATEPART(week, RegistrationDate);

SQL - GROUP BY 3 values of the same column

I have this table in GBQ :
ClientID Type Month
XXX A 4
YYY C 4
FFX B 5
FFF B 6
XXX C 6
XXX A 6
YRE C 7
AAR A 7
FFF A 8
EGT B 8
FFF B 9
ETT C 9
I am counting the number of Type per ClientID and Month, with this basic query :
SELECT ClientID,
COUNT(DISTINCT Type) NbTypes,
Month
FROM Table
GROUP BY ClientID, Month
The result looks like this :
ClientID NbTypes Month
XXX 1 4
XXX 2 6
FFF 1 6
FFF 1 8
FFF 1 9
... ... ...
What I need to do is, count the number of Type per ClientID and for each Month : per the last 3 months.
For example :
For the ClientID = XXX, and Month = 8 : I want to have the count of Type where Month = 6 AND Month = 7 AND Month = 8
Is there a way to do this with GROUP BY ?
Thank you
You could use HAVING in your statement:
SELECT ClientID,
COUNT(DISTINCT Type) NbTypes,
Month
FROM Table
GROUP BY ClientID, Month
HAVING Month = EXTRACT(MONTH FROM CURRENT_DATE())
OR Month = EXTRACT(MONTH FROM DATE_SUB(DATE_TRUNC(CURRENT_DATE(), MONTH), INTERVAL 1 MONTH))
OR Month = EXTRACT(MONTH FROM DATE_SUB(DATE_TRUNC(CURRENT_DATE(), MONTH), INTERVAL 2 MONTH))
Note that in your table seems to be no column to determinate the year, so this statement will group all values with month value of the current month to current month minus two months. So for example every data from December, November and October 2021, 2020, 2019 etc. will be selected with this query.
Also note that I could not test this statement, since I don't use BigQuery.
Here is the source for the Date-Functions:
https://cloud.google.com/bigquery/docs/reference/standard-sql/date_functions
You can use a SELECT in a SELECT if that is allowed in Google Big Query
SELECT ClientID,
COUNT(DISTINCT Type) NbTypes,
Month,
MAX((select count(distinct Type)
from Table t2
where t1.ClientID=t2.ClientID
and t1.month-t2.month between 0 and 2
)
) as NbType_3_months
FROM Table t1
GROUP BY ClientID, Month
You can group rows by ClientID and Month, count the number of types and sort rows by ClientID in ascending order and by Month in descending order, and then select from each group the rows of the past three months. It is roundabout and complicated to handle such a scenario in SQL because SQL implements set-orientation only halfway up. For your case, you have to get the largest month for each ClientID, find the eligible records through a join filter, and perform grouping and count. The usual way is to fetch the original data out of the database and process it in Python or SPL. SPL, the open-source Java package, is easier to be integrated into a Java program and generate much simpler code. It gets the task done with only two lines of code:
A
1
=GBQ.query("SELECT CLIENTID, COUNT(DISTINCT TYPE) AS NBTYPES, MONTH FROM t2 GROUP BY CLIENTID, MONTH ORDER BY CLIENTID, MONTH DESC")
2
=A1.group#o(#1).run(m=~.#3-3,~=~.select(MONTH>m)).conj()

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

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!!

SQL How to calculate Average time between Order Purchases? (do sql calculations based on next and previous row)

I have a simple table that contains the customer email, their order count (so if this is their 1st order, 3rd, 5th, etc), the date that order was created, the value of that order, and the total order count for that customer.
Here is what my table looks like
Email Order Date Value Total
r2n1w#gmail.com 1 12/1/2016 85 5
r2n1w#gmail.com 2 2/6/2017 125 5
r2n1w#gmail.com 3 2/17/2017 75 5
r2n1w#gmail.com 4 3/2/2017 65 5
r2n1w#gmail.com 5 3/20/2017 130 5
ation#gmail.com 1 2/12/2018 150 1
ylove#gmail.com 1 6/15/2018 36 3
ylove#gmail.com 2 7/16/2018 41 3
ylove#gmail.com 3 1/21/2019 140 3
keria#gmail.com 1 8/10/2018 54 2
keria#gmail.com 2 11/16/2018 65 2
What I want to do is calculate the time average between purchase for each customer. So lets take customer ylove. First purchase is on 6/15/18. Next one is 7/16/18, so thats 31 days, and next purchase is on 1/21/2019, so that is 189 days. Average purchase time between orders would be 110 days.
But I have no idea how to make SQL look at the next row and calculate based on that, but then restart when it reaches a new customer.
Here is my query to get that table:
SELECT
F.CustomerEmail
,F.OrderCountBase
,F.Date_Created
,F.Total
,F.TotalOrdersBase
FROM #FullBase F
ORDER BY f.CustomerEmail
If anyone can give me some suggestions, that would be greatly appreciated.
And then maybe I can calculate value differences (in percentage). So for example, ylove spent $36 on their first order, $41 on their second which is a 13% increase. Then their second order was $140 which is a 341% increase. So on average, this customer increased their purchase order value by 177%. Unrelated to SQL, but is this the correct way of calculating a metric like this?
looking to your sample you clould try using the diff form min and max date divided by total
select email, datediff(day, min(Order_Date), max(Order_Date))/(total-1) as avg_days
from your_table
group by email
and for manage also the one order only
select email,
case when total-1 > 0 then
datediff(day, min(Order_Date), max(Order_Date))/(total-1)
else datediff(day, min(Order_Date), max(Order_Date)) end as avg_days
from your_table
group by email
The simplest formulation is:
select email,
datediff(day, min(Order_Date), max(Order_Date)) / nullif(total-1, 0) as avg_days
from t
group by email;
You can see this is the case. Consider three orders with od1, od2, and od3 as the order dates. The average is:
( (od2 - od1) + (od3 - od2) ) / 2
Check the arithmetic:
--> ( od2 - od1 + od3 - od2 ) / 2
--> ( od3 - od1 ) / 2
This pretty obviously generalizes to more orders.
Hence the max() minus min().

How to aggregate 7 days in SQL

I was trying to aggregate a 7 days data for FY13 (starts on 10/1/2012 and ends on 9/30/2013) in SQL Server but so far no luck yet. Could someone please take a look. Below is my example data.
DATE BREAD MILK
10/1/12 1 3
10/2/12 2 4
10/3/12 2 3
10/4/12 0 4
10/5/12 4 0
10/6/12 2 1
10/7/12 1 3
10/8/12 2 4
10/9/12 2 3
10/10/12 0 4
10/11/12 4 0
10/12/12 2 1
10/13/12 2 1
So, my desired output would be like:
DATE BREAD MILK
10/1/12 1 3
10/2/12 2 4
10/3/12 2 3
10/4/12 0 4
10/5/12 4 0
10/6/12 2 1
Total 11 15
10/7/12 1 3
10/8/12 2 4
10/9/12 2 3
10/10/12 0 4
10/11/12 4 0
10/12/12 2 1
10/13/12 2 1
Total 13 16
--------through 9/30/2013
Please note, since FY13 starts on 10/1/2012 and ends on 9/30/2012, the first week of FY13 is 6 days instead of 7 days.
I am using SQL server 2008.
You could add a new computed column for the date values to group them by week and sum the other columns, something like this:
SELECT DATEPART(ww, DATEADD(d,-2,[DATE])) AS WEEK_NO,
SUM(Bread) AS Bread_Total, SUM(Milk) as Milk_Total
FROM YOUR_TABLE
GROUP BY DATEPART(ww, DATEADD(d,-2,[DATE]))
Note: I used DATEADD and subtracted 2 days to set the first day of the week to Monday based on your dates. You can modify this if required.
Use option with GROUP BY ROLLUP operator
SELECT CASE WHEN DATE IS NULL THEN 'Total' ELSE CONVERT(nvarchar(10), DATE, 101) END AS DATE,
SUM(BREAD) AS BREAD, SUM(MILK) AS MILK
FROM dbo.test54
GROUP BY ROLLUP(DATE),(DATENAME(week, DATE))
Demo on SQLFiddle
Result:
DATE BREAD MILK
10/01/2012 1 3
10/02/2012 2 4
10/03/2012 2 3
10/04/2012 0 4
10/05/2012 4 0
10/06/2012 2 1
Total 11 15
10/07/2012 1 3
10/08/2012 4 7
10/10/2012 0 4
10/11/2012 4 0
10/12/2012 2 1
10/13/2012 2 1
Total 13 16
You are looking for a rollup. In this case, you will need at least one more column to group by to do your rollup on, the easiest way to do that is to add a computed column that groups them into weeks by date.
Take a lookg at: Summarizing Data Using ROLLUP
Here is the general idea of how it could be done:
You need a derived column for each row to determine which fiscal week that record belongs to. In general you could subtract that record's date from 10/1, get the number of days that have elapsed, divide by 7, and floor the result.
Then you can GROUP BY that derived column and use the SUM aggregate function.
The biggest wrinkle is that 6 day week you start with. You may have to add some logic to make sure that the weeks start on Sunday or whatever day you use but this should get you started.
The WITH ROLLUP suggestions above can help; you'll need to save the data and transform it as you need.
The biggest thing you'll need to be able to do is identify your weeks properly. If you don't have those loaded into tables already so you can identify them, you can build them on the fly. Here's one way to do that:
CREATE TABLE #fy (fyear int, fstart datetime, fend datetime);
CREATE TABLE #fylist(fyyear int, fydate DATETIME, fyweek int);
INSERT INTO #fy
SELECT 2012, '2011-10-01', '2012-09-30'
UNION ALL
SELECT 2013, '2012-10-01', '2013-09-30';
INSERT INTO #fylist
( fyyear, fydate )
SELECT fyear, DATEADD(DAY, Number, DATEADD(DAY, -1, fy.fstart)) AS fydate
FROM Common.NUMBERS
CROSS APPLY (SELECT * FROM #fy WHERE fyear = 2013) fy
WHERE fy.fend >= DATEADD(DAY, Number, DATEADD(DAY, -1, fy.fstart));
WITH weekcalc AS
(
SELECT DISTINCT DATEPART(YEAR, fydate) yr, DATEPART(week, fydate) dt
FROM #fylist
),
ridcalc AS
(
SELECT
ROW_NUMBER() OVER (ORDER BY yr, dt) AS rid, yr, dt
FROM weekcalc
)
UPDATE #fylist
SET fyweek = rid
FROM #fylist
JOIN ridcalc
ON DATEPART(YEAR, fydate) = yr
AND DATEPART(week, fydate) = dt;
SELECT list.fyyear, list.fyweek, p.[date], COUNT(bread) AS Bread, COUNT(Milk) AS Milk
FROM products p
JOIN #fylist list
ON p.[date] = list.fydate
GROUP BY list.fyyear, list.fyweek, p.[date] WITH ROLLUP;
The Common.Numbers reference above is a simple numbers table that I use for this sort of thing (goes from 1 to 1M). You could also build that on the fly as needed.