Get last 7 days, 20 days and YTD count - sql

I have a table with columns sales_date, sales_id, sales_region and I am looking to display the count of sales for the past 7 days, 20 days and YTD.
I have this query below that returns the correct count for 7 and 20 days but the YTD shows the count minus the 7 and 20 days. How can I tweak this query to show the YTD correctly? Thank you
select region,
case when current_date- sales_date <=7 then 'Past7'
when current_date- sales_date <=28 then 'Past20'
else 'YTD'
end as "trendsales",
count(*) as salescount
from sales_table
where sales_date >= '2022-01-01'
group by 1

you could pivot it a bit. 4 columns Region, YTD, Past7, and Past20 would be columns.
select region,
sum(case when current_date- sales_date <=7 then 1 else 0 end) as Past7,
sum(case when current_date- sales_date <=28 then 1 else 0 end) as Past20,
count(*) as YTD
from sales_table
where sales_date >= '2022-01-01'
group by 1

Related

How do I get transactions amount > 1000 of all months in SQL

I have been trying to pull customers who have transaction amount greater than 1000 in all months. This is what I have tried so far. But it doesn't look like it's working when I do individual customer test.
Select customer
,extract(month from trans_date) as mth
,extract(year from trans_date) as yr
,sum(trans_amount) as amt
, case when mth in (8) and amt > 1000 then 1 else 0 end as aug
, case when mth in (9) and amt > 1000 then 1 else 0 end as sep
, case when mth in (10) and amt > 1000 then 1 else 0 end as oct
, case when mth in (11) and amt > 1000 then 1 else 0 end as nov
, case when mth in (12) and amt > 1000 then 1 else 0 end as de_c
from transaction
group by 1,2,3
having (aug = 1 and sep = 1 and oct=1 and nov=1 and de_c = 1)
Select customer
,extract(month from trans_date) as mth
,extract(year from trans_date) as yr
,sum(trans_amount) as amt
from transaction
-- filter only those months you want to check, e.g.
where trans_date between date '2021-08-01' and date '2021-12-31'
group by 1,2,3
-- check that every month there was an individual transaction over 1000
qualify
min(max(trans_amount))
over (partition by customer) > 1000
Edit:
Same logic to get just the customer without detail rows:
select customer
from
(
Select customer, max(trans_amount) as maxamt
from transaction
-- filter only those months you want to check, e.g.
where trans_date between date '2021-08-01' and date '2021-12-31'
group by
customer
,trunc(trans_date, 'mon') -- for every month
) as dt
group by customer
-- check that every month there was an individual transaction over 1000
having min(maxamt) > 1000
You may want to try using Over (partition by) something like this.
Select customer
,extract(month from trans_date) as mth
,extract(year from trans_date) as yr
,sum(trans_amount) over (partition by customer , extract(month from trans_date)) as
total
From transaction
Order by total desc
Assuming your data is one record per customer per month.
To get unique customers where trans_amt > 1000 in every month :
select customer
from transaction
group by customer
having count(1) = count(case when trans_amt > 1000 then 1 else 0 end)
To get all records only for customers where trans_amt > 1000 in every month :
select customer, trans_date, trans_amt
from transaction
qualify count(1) over (partition by customer) = count(case when trans_amt > 1000 then 1 else 0 end) over (partition by customer)

I am looking to find customers repurchase frequency in SQL from their first purchase date

I am trying to find the customer's repurchase rates from their first order date. For example, for 2016, how many customer purchased 1X in days 1-365 from their initial purchase, how many purchased twice etc.
I have a transaction_detail table which looks like below:
txn_date Customer_ID Transaction_Number Sales
1/2/2019 1 12345 $10
4/3/2018 1 65890 $20
3/22/2019 3 64453 $30
4/3/2019 4 88567 $20
5/21/2019 4 85446 $15
1/23/2018 5 89464 $40
4/3/2019 5 99674 $30
4/3/2019 6 32224 $20
1/23/2018 6 46466 $30
1/20/2018 7 56558 $30
I am able to find the customers who have shopped in 2016 and how many times have they repurchased in 2016, but I need to find the customer who have shopped in 2016 and how many times have they come back from their first purchase date.
I need a starting point for the query, I am not sure how to build this logic in my SQL code.
Any help would be appreciated.
I am using the below query:
WITH by_year
AS (SELECT
Customer_ID,
to_char(txn_date, 'YYYY') AS visit_year
FROM table
GROUP BY Customer_ID, to_char(txn_date, 'YYYY')),
with_first_year
AS (SELECT
Customer_ID,
visit_year,
FIRST_VALUE(visit_year) OVER (PARTITION BY Customer_ID ORDER BY visit_year) AS first_year
FROM by_year),
with_year_number
AS (SELECT
Customer_ID,
visit_year,
first_year,
(visit_year - first_year) AS year_number
FROM with_first_year)
SELECT
first_year AS first_year,
SUM(CASE WHEN year_number = 0 THEN 1 ELSE 0 END) AS year_0,
SUM(CASE WHEN year_number = 1 THEN 1 ELSE 0 END) AS year_1,
SUM(CASE WHEN year_number = 2 THEN 1 ELSE 0 END) AS year_2,
SUM(CASE WHEN year_number = 3 THEN 1 ELSE 0 END) AS year_3,
SUM(CASE WHEN year_number = 4 THEN 1 ELSE 0 END) AS year_4,
SUM(CASE WHEN year_number = 5 THEN 1 ELSE 0 END) AS year_5,
SUM(CASE WHEN year_number = 6 THEN 1 ELSE 0 END) AS year_6,
SUM(CASE WHEN year_number = 7 THEN 1 ELSE 0 END) AS year_7,
SUM(CASE WHEN year_number = 8 THEN 1 ELSE 0 END) AS year_8,
SUM(CASE WHEN year_number = 9 THEN 1 ELSE 0 END) AS year_9
FROM with_year_number
GROUP BY first_year
ORDER BY first_year
Use window functions and aggregation:
select cnt, count(*), min(customer_id), max(customer_id)
from (select customer_id, count(*) as cnt
from (select td.*,
min(txn_date) over (partition by Customer_ID) as min_txn_date
from transaction_detail td
) td
where txn_date >= min_txn_date and txn_date < min_txn_date + interval '365' day
group by customer_id
) c
group by cnt
order by cnt;
So as per my understanding, you want to know the count of the distinct person who first purchased in 2016 and repurchased after one year or more from date of purchase.
Select * from
(
Select customer_id,
Floor(months_between(txn_date, lead_txn_date)/12) as num_years
From
(
Select customer_id,
txn_date,
row_number() over (partition by Customer_ID order by txn_date) as rn,
lead(txn_date) over (partition by Customer_ID order by txn_date) as lead_txn_date
From your_table
)
Where txn_date >= date '2016-01-01'
and txn_date < date '2017-01-01'
and rn = 1
And months_between(txn_date, lead_txn_date) >= 12
)
Pivot
(
Count(1) for num_year in (1,2,3,4)
)
Ultimately, we are finding the number of years between first and second purchase of the customer. And first purchase must be in 2016.
Cheers!!

Split number to ranges in SQL

I am trying to write a SQL query that show the distribution of customers seniority since 2015/01/31:
Up to one month
Between one and six months
Between six months and one year
Over a year
I succeeded to split and group the number of months of the customers.
SELECT Seniority, COUNT(Customer_ID) [Number of Customers]
FROM
(SELECT Customer_ID,
DATEDIFF(MONTH, MIN(CONVERT(datetime, Order_Date)), '2015/01/31') Seniority
FROM Orders
GROUP BY Customer_ID) t
GROUP BY Seniority
How can I split by given ranges?
Expected:
Seniorty | Number of Customers
Up to one month | 0
Between one and six months | 14
Between six months and one year | 1
Over a year | 0
Use conditional aggregation:
WITH cte AS (
SELECT
'Up to one month' AS Seniority,
COUNT(CASE WHEN DATEDIFF(MONTH, Order_Date, '2015-01-31') < 1 THEN 1 END) AS [Number of Customers],
1 AS position
FROM Orders
UNION ALL
SELECT
'Between one and six months',
COUNT(CASE WHEN DATEDIFF(MONTH, Order_Date, '2015-01-31') >= 1 AND
DATEDIFF(MONTH, Order_Date, '2015-01-31') < 6 THEN 1 END),
2
FROM Orders
UNION ALL
SELECT
'Between six months and one year',
COUNT( CASE WHEN DATEDIFF(MONTH, Order_Date, '2015-01-31') >= 6 AND
DATEDIFF(MONTH, Order_Date, '2015-01-31') < 12 THEN 1 END),
3
FROM Orders
UNION ALL
SELECT
'Over a year',
COUNT(CASE WHEN DATEDIFF(MONTH, Order_Date, '2015-01-31') > 12 THEN 1 END),
4
FROM Orders
)
SELECT
Seniority,
[Number of Customers]
FROM cte
ORDER BY
position;
This answer assumes that the Order_Date column is already date or datetime. If not, then the first thing you should do is to convert this column to an actual date type.
WITH CTE AS (
SELECT CUSTOMER_ID,
CASE WHEN DATEDIFF(MONTH,'2015-01-31',ORDER_DATE)=1 THEN 'Up to one month'
WHEN DATEDIFF(MONTH,'2015-01-31',ORDER_DATE) BETWEEN 1 AND 6 THEN 'Between one and six months'
WHEN DATEDIFF(MONTH,'2015-01-31',ORDER_DATE) BETWEEN 6 AND 12 THEN 'Between six months and one year'
WHEN DATEDIFF(MONTH,'2015-01-31',ORDER_DATE)>12 THEN 'Over a year'
END AS SENIORITY
FROM ORDERS
)
SELECT SENIORITY AS 'Seniority', COUNT(CUSTOMER_ID) AS 'Number of Customers'
FROM CTE
WHERE SENIORITY IS NOT NULL
GROUP BY SENIORITY

Sum Based on Date

I currently have this code that I want to sum every quantity based on the year. I have written a code that I thought would sum all the charges in 2016 and 2017, but it isn't running correctly.
I added the two different types of partition by statements to test and see if either would work and they don't. When I take them out, the Annual column just shows me the quantity for that specific receipt.
Here is my current code:
SELECT
ReceiptNumber
,Quantity
,Date
,sum(CASE WHEN (Date >= '2016-01-01' and Date < '2017-01-01') THEN
Quantity
ELSE 0 END)
OVER (PARTITION BY Date)
as Annual2016
,sum(CASE WHEN (Date >= '2017-01-01' and Date < '2018-01-01') THEN
Quantity
ELSE 0 END)
OVER (PARTITION BY ReceiptNumber)
as Annual2017
FROM Table1
GROUP BY ReceiptNumber, Quantity, Date
I would like my data to look like this
ReceiptNumber Quantity Date Annual2016 Annual2017
1 5 2016-01-05 17 13
2 11 2017-04-03 17 13
3 12 2016-11-11 17 13
4 2 2017-09-09 17 13
Here is a sample of some of the data I am pulling from:
ReceiptNumber Quantity Date
1 5 2016-01-05
2 11 2017-04-03
3 12 2016-11-11
4 2 2017-09-09
5 96 2015-07-08
6 15 2016-12-12
7 24 2016-04-19
8 31 2017-01-02
9 10 2017-0404
10 18 2015-10-10
11 56 2017-06-02
Try something like this
Select
..
sum(CASE WHEN (Date >= '2016-01-01' and Date < '2017-01-01') THEN
Quantity
ELSE 0 END)
OVER () as Annual2016
sum(CASE WHEN (Date >= '2017-01-01' and Date < '2018-01-01') THEN
Quantity
ELSE 0 END)
OVER ()as Annual2017
..
Where Date >= '2016-01-01' and Date < '2018-01-01'
If you want it printed only once at the top then you should run it in a separate query like:
SELECT YEAR(Date) y, sum(Quantity) s FROM Table1 GROUP BY YEAR(Date)
and then do the main query like this:
SELECT * FROM table1
Easy, peasey ... ;-)
Your original question could also be answered with:
SELECT *,
(SELECT SUM(Quantity) FROM Table1 WHERE YEAR(Date)=2016 ) Annual2016,
(SELECT SUM(Quantity) FROM Table1 WHERE YEAR(Date)=2017 ) Annual2017
FROM table1
You need some conditional aggreation over a Window Aggregate. Simply remove both PARTITION BY as you're already filtering the year in the CASE:
SELECT
ReceiptNumber
,Quantity
,Date
,sum(CASE WHEN (Date >= '2016-01-01' and Date < '2017-01-01') THEN
Quantity
ELSE 0 END)
OVER () as Annual2016
,sum(CASE WHEN (Date >= '2017-01-01' and Date < '2018-01-01') THEN
Quantity
ELSE 0 END)
OVER () as Annual2017
FROM Table1
You probably don't need the final GROUP BY ReceiptNumber, Quantity, Date

SQL - How to count records for each status in one line per day?

I have a table Sales
Sales
--------
id
FormUpdated
TrackingStatus
There are several status e.g. Complete, Incomplete, SaveforLater, ViewRates etc.
I want to have my results in this form for the last 8 days(including today).
Expected Result:
Date Part of FormUpdated, Day of Week, Counts of ViewRates, Counts of Sales(complete), Counts of SaveForLater
--------------------------------------
2015-05-19 Tuesday 3 1 21
2015-05-18 Monday 12 5 10
2015-05-17 Sunday 6 1 8
2015-05-16 Saturday 5 3 7
2015-05-15 Friday 67 5 32
2015-05-14 Thursday 17 0 5
2015-05-13 Wednesday 22 0 9
2015-05-12 Tuesday 19 2 6
Here is my sql query:
select datename(dw, FormUpdated), count(ID), TrackingStatus
from Sales
where FormUpdated <= GETDATE()
AND FormUpdated >= GetDate() - 8
group by datename(dw, FormUpdated), TrackingStatus
order by datename(dw, FormUpdated) desc
I do not know how to make the next step.
Update
I forgot to mention, I only need the Date part of the FormUpdated, not all parts.
You can use SUM(CASE WHEN TrackingStatus = 'SomeTrackingStatus' THEN 1 ELSE 0 END)) to get the status count for each tracking status in individual column. Something like this. SQL Fiddle
select
CONVERT(DATE,FormUpdated) FormUpdated,
DATENAME(dw, CONVERT(DATE,FormUpdated)),
SUM(CASE WHEN TrackingStatus = 'ViewRates' THEN 1 ELSE 0 END) c_ViewRates,
SUM(CASE WHEN TrackingStatus = 'Complete' THEN 1 ELSE 0 END) c_Complete,
SUM(CASE WHEN TrackingStatus = 'SaveforLater' THEN 1 ELSE 0 END) c_SaveforLater
from Sales
where FormUpdated <= GETDATE()
AND FormUpdated >= DATEADD(D,-8,GetDate())
group by CONVERT(DATE,FormUpdated)
order by CONVERT(DATE,FormUpdated) desc
You can also use a PIVOT to achieve this result - you'll just need to complete the list of TrackingStatus names in both the SELECT and the FOR, and no GROUP BY required:
WITH DatesOnly AS
(
SELECT Id, CAST(FormUpdated AS DATE) AS DateOnly, DATENAME(dw, FormUpdated) AS DayOfWeek, TrackingStatus
FROM Sales
)
SELECT DateOnly, DayOfWeek,
-- List of Pivoted Columns
[Complete],[Incomplete], [ViewRates], [SaveforLater]
FROM DatesOnly
PIVOT
(
COUNT(Id)
-- List of Pivoted columns
FOR TrackingStatus IN([Complete],[Incomplete], [ViewRates], [SaveforLater])
) pvt
WHERE DateOnly <= GETDATE() AND DateOnly >= GetDate() - 8
ORDER BY DateOnly DESC
SqlFiddle
Also, I think your ORDER BY is wrong - it should just be the Date, not day of week.