Count occurence of specific code per customer in 6 month period - sql

I have a table that contains the following:
customerid | date (dmy) | productid
John | 1-3-14 | A
John | 7-5-14 | Y
John | 8-5-14 | Y
John | 1-10-15 | B
John | 1-11-15 | Y
Pete | 1-7-15 | Y
I need to find out how often customer X has bought Product Y in a six-month period.
The start of a period is defined as the first time a customer has bought one of the products A,B, C or Y. The endtime of a period is exactly six months after that.
The next period starts when the customer buys again one of the products A,B,C or Y.
So the output should be
customerid | period-start | period-end | countofY
John | 1-3-14 | 8-5-14 | 2
John | 1-10-15 | 1-11-15 | 1
Pete | 1-7-15 | 1-7-15 | 1

SELECT c.Customerid, MIN(c.pdate) AS startperiod, c1.endperiod,
(
SELECT COUNT(temp.productid) FROM Customer temp
WHERE temp.Customerid = c.Customerid
AND temp.pdate >= MIN(c.pdate)
AND temp.pdate <= c1.endperiod
GROUP BY temp.productid HAVING temp.productid ='Y'
)AS countOfY
FROM Customer c
CROSS APPLY
(
SELECT TOP 1 c1.pdate AS endperiod
FROM Customer c1
WHERE c1.Customerid = c.Customerid
AND c1.pdate >= c.pdate
AND
(
DATEDIFF(MONTH, c.pdate, c1.pdate) < 6
OR
(
SELECT TOP 1 t.pdate FROM Customer t
WHERE t.Customerid = c.Customerid
AND t.pdate < c1.pdate
) IS NULL
)
ORDER BY c1.pdate DESC
)AS c1 GROUP BY c1.endperiod, c.Customerid

;WITH CTE_DateRanges AS (
SELECT
customerid,
productid,
MIN(purchase_date) AS period_start,
DATEADD(MM, 6, MIN(purchase_date)) AS period_end
FROM
My_Table
GROUP BY
customerid,
productid
)
SELECT
DR.customerid,
DR.productid,
DR.period_start,
DR.period_end,
COUNT(*)
FROM
CTE_DateRanges DR
INNER JOIN My_Table MT ON
MT.customerid = DR.customerid AND
MT.productid = DR.productid AND
MT.purchase_date BETWEEN DR.period_start AND DR.period_end
GROUP BY
DR.customerid,
DR.productid,
DR.period_start,
DR.period_end,

Related

Need help for MS Access Select Request using 2 tables

For a "products reservation system", I have 2 tables :
"RD", for global reservations data (fieds: ID, CustomerID, Date, ...)
"RP", for reserved products data per reservation (fields: ID, RD_ID, ProductID, Status, ...). RD_ID fits with the ID in RD table (field for joining). Status field can have these values: O, C, S.
I need to extract (with 2 Select instructions) the list of reservations and the number of reservations for which all products have status 'O' .
Data example for RP:
ID | RD_ID | ProdID | Status
----------------------------
1 | 1 | 100 | O
2 | 1 | 101 | O
3 | 1 | 102 | O
4 | 2 | 105 | O
5 | 2 | 100 | S
6 | 3 | 101 | C
7 | 3 | 102 | O
In this example, Select statement should return only RD_ID 1
For the number of ID, the following request does not work because it also includes reservations with products having different status:
SELECT COUNT(rd.ID) FROM rd INNER JOIN rp ON rp.RD_ID = rd.ID WHERE rp.Status = 'O';
Could you help me for the right Select statement?
Thank you.
SELECT rd.ID, COUNT(rd.ID) CountOfRD, status
FROM rd INNER JOIN rp ON rp.RD_ID
GROUP BY rd.ID, status
Use not exists as follows:
Select t.* from your_table t
Where t.status = 'O'
And not exists (select 1 from your_table tt
Where t.rd_id = tt.rd_id
And t.status != tt.status)
You can also use group by and having as follows:
Select rd_id
From your_table t
Group by rd_id
Having sum(case when status <> 'O' then 1 end) > 0

Select row that has max total value SQL Server

I have the following scheme (2 tables):
Customer (Id, Name) and
Sale (Id, CustomerId, Date, Sum)
How to select the following data ?
1) Best customer of all time (Customer, which has Max Total value in the Sum column)
For example, I have 2 tables (Customers and Sales respectively):
id CustomerName
---|--------------
1 | First
2 | Second
3 | Third
id CustomerId datetime Sum
---|----------|------------|-----
1 | 1 | 04/06/2013 | 50
2 | 2 | 04/06/2013 | 60
3 | 3 | 04/07/2013 | 30
4 | 1 | 03/07/2013 | 50
5 | 1 | 03/08/2013 | 50
6 | 2 | 03/08/2013 | 30
7 | 3 | 24/09/2013 | 20
Desired result:
CustomerName TotalSum
------------|--------
First | 150
2) Best customer of each month in the current year (the same as previous but for each month in the current year)
Thanks.
Try this for the best customer of all times
SELECT Top 1 WITH TIES c.CustomerName, SUM(s.SUM) AS TotalSum
FROM Customer c JOIN Sales s ON s.CustomerId = c.CustomerId
GROUP BY c.CustomerId, c.CustomerName
ORDER BY SUM(s.SUM) DESC
One option is to use RANK() combined with the SUM aggregate. This will get you the overall values.
select customername, sumtotal
from (
select c.customername,
sum(s.sum) sumtotal,
rank() over (order by sum(s.sum) desc) rnk
from customer c
join sales s on c.id = s.customerid
group by c.id, c.customername
) t
where rnk = 1
SQL Fiddle Demo
Grouping this by month and year should be trivial at that point.

SQL Total Sale calculation

I have a sale table includes purchases/returns/exchanges
Sample:
--------**saleTbl**------------
CustID | DOP | SKU | Price
111 | 11/05/12 | 001 | 45.99
222 | 11/20/12 | 001 | 45.99
111 | 11/06/12 | 002 | 40.95
111 | 11/06/12 | 001 | -45.99
111 | 11/19/12 | 004 | 50.00
222 | 11/25/12 | 003 | 20.99
111 | 12/01/12 | 002 | -40.95
111 | 12/01/12 | 003 | 20.99
Criteria is: find total for each customer during 11/05/12 - 11/20/12. If customer exchanged the item that was purchased during that time and purchase with the same day will be count.
The expected result is:
CustID | DOP | Price
222 | 11/20/12 | 45.99
111 | 12/01/12 | 70.99
I have tried to get the total but of course it is not right:
SELECT DISTINCT [num_cp] AS 'Member Id'
,MAX([dop]) AS 'Date'
,SUM([price]) AS 'Point'
FROM [Mailing_List].[dbo].[UGG_DoublePoint]
WHERE [num_cp] IN
(
SELECT [num_cp]
FROM [Mailing_List].[dbo].[UGG_DoublePoint]
GROUP BY [num_cp]
HAVING SUM([price]) >0
)
--AND
AND [dop] BETWEEN '11/05/12' AND '11/20/12'
GROUP BY [num_cp]
Please help! Thanks everyone.
I think you need to change your query to this
; WITH CTE AS
(
SELECT *,
COUNT(*) OVER (PARTITION BY CustID, DOP) Row_Cnt
FROM TEST
), CTE2 AS
(
SELECT * FROM CTE
WHERE [dop] BETWEEN '11/05/12' AND '11/20/12'
), CTE3 AS
(
SELECT * FROM CTE2 WHERE price > 0
UNION
SELECT * FROM CTE2 WHERE price < 0
and SKU IN (SELECT SKU FROM CTE2 WHERE Price > 0)
UNION
SELECT * FROM CTE
WHERE row_cnt > 1 and DOP IN (
SELECT max(A.dop) d FROM CTE A
INNER JOIN CTE2 B ON A.CustID = B.CustID AND A.SKU = B.SKU
)
)
SELECT Custid, max(dop) dateid, sum(price) Price
from cte3
group by custid;
Check SQL Fiddle Demo
I think this will work. It basically filters out any returns from the result set by use of a left join.
NOTE: There would be an issue with this in the case that someone purchased/returned multiple SKUs of the same thing on the same day.
select pur.CustId, sum(pur.price) TotalPrice
from test pur
left join test ret
on pur.custid = ret.custid
and pur.dop = ret.dop
and pur.sku = ret.sku
and pur.price = (-1 * ret.price)
where pur.dop between '11/05/2012' AND '11/20/2012'
and ret.price is null
group by pur.CustId

MAX values over partition by ()

I have monthly sales over specific car brands, and every month i want the max 5 car brands in relation to the sales. Then, next to each of these max brands, i want the number (if there is) that indicates how many times this specific brand was in the top five the previous 4 months.
Foe example, if table data is:
Timestamp | Brand | Sales
1/1/2012 | A | 23
1/1/2012 | B | 45
1/1/2012 | C | 11
1/1/2012 | D | 3
1/1/2012 | E | 55
1/1/2012 | F | 1
1/1/2012 | G | 22
---------------------------
1/2/2012 | A | 93
1/2/2012 | B | 35
1/2/2012 | C | 01
1/2/2012 | D | 100
1/2/2012 | E | 45
1/2/2012 | F | 77
1/2/2012 | G | 12
for a two month data, the query output for February (examining only Feb and Jan) would be :
Max_ Brand_Sales| Reappearance_Factor
--------------------------------------
E | 1
B | 1
D | 0
F | 0
A | 1
Select
c.Brand,
nvl(Count(p.Brand), 0) As Reappearance_Factor
From (
Select
Brand,
Rank () Over (Order By Sales Desc) as r
From
Sales
Where
Timestamp = Date '2012-02-01'
) c
left outer join (
Select
Brand,
Rank () Over (Partition By Timestamp Order By Sales Desc) as r
From
Sales
Where
Timestamp >= Date '2011-10-01' And
Timestamp < Date '2012-02-01'
) p
on c.Brand = p.Brand And p.r <= 5
Where
c.r <= 5
Group By
c.Brand
http://sqlfiddle.com/#!4/46770/21
Try this:
1) Query that calculates monthly rank for every brand:
SELECT
s.Brand,
trunc(s.Timestamp,'MONTH') month_start,
rank() OVER (PARTITION BY trunc(s.Timestamp,'MONTH')
ORDER BY s.Sales DESC) as monthly_rank
FROM Sales s;
2) Query that outputs the top 5 brands for current month:
SELECT
t.Brand
FROM
(
SELECT
s.Brand,
trunc(s.Timestamp,'MONTH') month_start,
rank() OVER (PARTITION BY trunc(s.Timestamp,'MONTH')
ORDER BY s.Sales DESC) as monthly_rank
FROM Sales s
) t
WHERE monthly_rank <= 5
AND month_start = trunc(sysdate,'MONTH');
3) Query to calculate "Reappearance" for past 4 month
SELECT
t.Brand,
count(*) as top
FROM
(
SELECT
s.Brand,
trunc(s.Timestamp,'MONTH') month_start,
rank() OVER (PARTITION BY trunc(s.Timestamp,'MONTH')
ORDER BY s.Sales DESC) as monthly_rank
FROM Sales s
) t
WHERE monthly_rank <= 5
AND t.month_start BETWEEN add_months(sysdate, -1)
AND add_months(sysdate, -5)
GROUP BY t.Brand;
4) Last thing to do - LEFT JOIN query 2 and 3
SQLFiddle here http://sqlfiddle.com/#!4/46770/65

Sql: Calc average times a customers ordered a product in a period

How would you calc how many times a product is sold in average in a week or month, year.
I'm not interested in the Amount, but how many times a customer has bought a given product.
OrderLine
OrderNo | ProductNo | Amount |
----------------------------------------
1 | 1 | 10 |
1 | 4 | 2 |
2 | 1 | 2 |
3 | 1 | 4 |
Order
OrderNo | OrderDate
----------------------------------------
1 | 2012-02-21
2 | 2012-02-22
3 | 2012-02-25
This is the output I'm looking for
ProductNo | Average Orders a Week | Average Orders a month |
------------------------------------------------------------
1 | 3 | 12 |
2 | 5 | 20 |
You would have to first pre-query it grouped and counted per averaging method you wanted. To distinguish between year 1 and 2, I would add year() of the transaction into the grouping qualifier for distinctness. Such as Sales in Jan 2010 vs Sales in 2011 vs 2012... similarly, week 1 of 2010, week 1 of 2011 and 2012 instead of counting as all 3 years as a single week.
The following could be done if you are using MySQL
select
PreCount.ProductNo,
PreCount.TotalCount / PreCount.CountOfYrWeeks as AvgPerWeek,
PreCount.TotalCount / PreCount.CountOfYrMonths as AvgPerMonth,
PreCount.TotalCount / PreCount.CountOfYears as AvgPerYear
from
( select
OL.ProductNo,
count(*) TotalCount,
count( distinct YEARWEEK( O.OrderDate ) ) as CountOfYrWeeks,
count( distinct Date_Format( O.OrderDate, "%Y%M" )) as CountOfYrMonths,
count( distinct Year( O.OrderDate )) as CountOfYears
from
OrderLine OL
JOIN Order O
on OL.OrderNo = O.OrderNo
group by
OL.ProductNo ) PreCount
This is a copy of DRapp's answer, but coded for SQL Server (it's too big for a comment!)
SELECT PreCount.ProductNo,
PreCount.TotalCount / PreCount.CountOfYrWeeks AS AvgPerWeek,
PreCount.TotalCount / PreCount.CountOfYrMonths AS AvgPerMonth,
PreCount.TotalCount / PreCount.CountOfYears AS AvgPerYear
FROM (SELECT OL.ProductNo,
Count(*) TotalCount,
Count(DISTINCT Datepart(wk, O.OrderDate)) AS CountOfYrWeeks,
Count(DISTINCT Datepart(mm, O.OrderDate)) AS CountOfYrMonths,
Count(DISTINCT Year(O.OrderDate)) AS CountOfYears
FROM OrderLine OL JOIN [Order] O
ON OL.OrderNo = O.OrderNo
GROUP BY OL.ProductNo) PreCount