Hi have written query like this:
select Customerid,orderDate, OrderNumber,
DENSE_RANK() OVER (PARTITION BY Customerid ORDER BY orderDate) "rank"
from [order]
and this produce result:
Here I want to retrieve only latest purchase of each customer like this:
1 2014-04-09 00:00:00.000 543141 6
2 2014-03-04 00:00:00.000 543056 4
3 2014-01-28 00:00:00.000 542986 7
How to achieve this using sql query
Use a subquery:
select o.*
from (select Customerid,orderDate, OrderNumber,
DENSE_RANK() OVER (PARTITION BY Customerid ORDER BY orderDate DESC) as seqnum
from [order] o
) o
where seqnum = 1;
Related
https://www.db-fiddle.com/f/rgLXTu3VysD3kRwBAQK3a4/3
My problem here is that I want function partition over to start counting the rows only from certain time range.
In this example, if I would add rn = 1 at the end, order_id = 5 would be excluded from the results (because partition is ordering by paid_date and there's order_id = 6 with earlier date) but it shouldn't be as I want that time range for partition starts from '2019-01-10'.
Adding condition rn = 1expected output should be order_id 3,5,11,15, now its only 3,11,15
it should include only orders with is_paid = 0 that are the first one within given time range (if there's preceeding order with is_paid = 1 it shouldn't be counted)
use correlated subquery with not exists
DEMO
SELECT order_id, customer_id, amount, is_paid, paid_date, rn FROM (
SELECT o.*,
ROW_NUMBER() OVER(PARTITION BY customer_id ORDER BY paid_date,order_id) rn
FROM orders o
WHERE paid_date between '2019-01-10'
and '2019-01-15'
) x where rn=1 and not exists (select 1 from orders o1 where x.order_id=o1.order_id
and is_paid=1)
OUTPUT:
order_id customer_id amount is_paid paid_date rn
3 101 30 0 10/01/2019 00:00:00 1
5 102 15 0 10/01/2019 00:00:00 1
11 104 31 0 10/01/2019 00:00:00 1
15 105 11 0 10/01/2019 00:00:00 1
If priority should be given to order_id then put that before paid date in the partition function order by clause, this will solve your issue.
SELECT order_id, customer_id, amount, is_paid, paid_date, rn FROM (
SELECT o.*,
ROW_NUMBER() OVER(PARTITION BY customer_id ORDER BY order_id,paid_date) rn
FROM orders o
) x WHERE is_paid = 0 and paid_date between
'2019-01-10' and '2019-01-15' and rn=1
Since you need the paid date to be ordered first you need to imply a where condition in the partitioning table in order to avoid unnecessary dates interrupting the partition function.
SELECT order_id, customer_id, amount, is_paid, paid_date, rn FROM (
SELECT o.*,
ROW_NUMBER() OVER(PARTITION BY customer_id ORDER BY paid_date, order_id) rn
FROM orders o
where paid_date between '2019-01-10' and '2019-01-15'
) x WHERE is_paid = 0 and rn=1
I'm trying to create a rolling total of the number of orders placed by a customer within a specific time period, ordered by date.
I have tried to use the partition function but the below query doesn't yield the correct results. Any help would be appreciated
select
CustomerID
, Order ID
, COUNT(OrderID) OVER (PARTITION BY CustomerID ORDER BY OrderDate) RunningOrderCount
from #existingtable
I want the results to be a table of all the customer ID's, all their corresponding order ID's and then a field with the order count eg...
CustomerID OrderID OrderCount
1234 5675 1
1234 5676 2
1234 5677 3
1234 5678 4
1234 5679 5
I think your are looking for is ROW_NUMBER()
SELECT
CustomerID
, OrderID
, ROW_NUMBER() OVER (PARTITION BY CustomerID ORDER BY OrderDate) RunningOrderCount
from #existingtable
this line will return you the count over a single year
select
CustomerID
, Order ID
, count(*) OVER
(PARTITION BY CustomerID ORDER BY DATEPART(yy,orderDate) ) as year_total
from #customtable
It's the same if you use row_number instead of count(*)
Using SQL Server 2016. I have a table:
Product Qty OrderDate
--------------------------
Toys 100 2018-10-01
Toys 100 2018-10-01
Books 30 2018-10-01
Toys 150 2018-10-02
Toys 50 2018-10-02
Toys 20 2018-10-02
Toys 110 2018-10-03
Toys 90 2018-10-04
Toys 200 2018-10-05
Toys 100 2018-10-05
Toys 30 2018-10-08
Toys 50 2018-10-09
and I want to calculate the average quantity per product, for the last 5 days. I am close to this with this query:
SELECT
Product,
RowNumber,
OrderDate,
AVG(TotalQty) OVER (ORDER BY RowNumber DESC ROWS 5 PRECEDING) as RollingAvg
FROM
(
SELECT ROW_NUMBER() OVER (PARTITION BY Product ORDER BY orderDate) AS RowNumber, Product, OrderDate, sum(Qty) as TotalQty
FROM Tbl
GROUP BY Product, OrderDate
) x
GROUP BY Product, RowNumber, OrderDate
The inner query works correctly, giving me the total per product/date pair. However my outer query reports a problem:
Column 'x.TotalQty' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
There's obviously something I'm doing wrong with my OVER clause, because when I remove that I get a valid result.
Syntactically valid query (that does the wrong thing):
SELECT
Product,
RowNumber,
OrderDate,
AVG(TotalQty) as RollingAvg
FROM
(
SELECT ROW_NUMBER() OVER (PARTITION BY Product ORDER BY orderDate) AS RowNumber, Product, OrderDate, sum(Qty) as TotalQty
FROM Tbl
GROUP BY Product, OrderDate
) x
GROUP BY Product, RowNumber, OrderDate
Any help/pointers would be much appreciated please - I'm close but can't cross this final hurdle!
I think you want:
SELECT Product, RowNumber, OrderDate,
AVG(TotalQty) OVER (ORDER BY RowNumber DESC ROWS 5 PRECEDING) as RollingAvg
FROM (SELECT ROW_NUMBER() OVER (PARTITION BY Product ORDER BY orderDate) AS RowNumber,
Product, OrderDate, sum(Qty) as TotalQty
FROM Tbl
GROUP BY Product, OrderDate
) x;
That is, the outer aggregation is unnecessary because AVG() is being used as a window function, not an aggregation function.
You should be able to do this without a subquery:
SELECT ROW_NUMBER() OVER (PARTITION BY Product ORDER BY orderDate) AS RowNumber,
Product, OrderDate, sum(Qty) as TotalQty,
AVG(SUM(Qty)) OVER (PARTITION BY Product ORDER BY orderDate ROWS BETWEEN 4 PRECEDING AND CURRENT ROW) as avg_5
FROM Tbl
GROUP BY Product, OrderDate;
Note that this interprets "last five days" as the current day plus the preceding four days. Your version has six days for the average.
I have a table including more than 5 million rows of sales transactions. I would like to find sum of date intervals between each customer three recent purchases.
Suppose my table looks like this :
CustomerID ProductID ServiceStartDate ServiceExpiryDate
A X1 2010-01-01 2010-06-01
A X2 2010-08-12 2010-12-30
B X4 2011-10-01 2012-01-15
B X3 2012-04-01 2012-06-01
B X7 2012-08-01 2013-10-01
A X5 2013-01-01 2015-06-01
The Result that I'm looking for may looks like this :
CustomerID IntervalDays
A 802
B 135
I know the query need to first retrieve 3 resent transactions of each customer (based on ServiceStartDate) and then calculate the interval between startDate and ExpiryDate of his/her transactions.
You want to calculate the difference between the previous row's ServiceExpiryDate and the current row's ServiceStartDate based on descending dates and then sum up the last two differences:
with cte as
(
select tab.*,
row_number()
over (partition by customerId
order by ServiceStartDate desc
, ServiceExpiryDate desc -- don't know if this 2nd column is necessary
) as rn
from tab
)
select t2.customerId,
sum(datediff(day, prevEnd, ServiceStartDate)) as Intervaldays
,count(*) as purchases
from cte as t2 left join cte as t1
on t1.customerId = t2.customerId
and t1.rn = t2.rn+1 -- previous and current row
where t2.rn <= 3 -- last three rows
group by t2.customerId;
Same result using LEAD:
with cte as
(
select tab.*,
row_number()
over (partition by customerId
order by ServiceStartDate desc) as rn
,lead(ServiceExpiryDate)
over (partition by customerId
order by ServiceStartDate desc
) as prevEnd
from tab
)
select customerId,
sum(datediff(day, prevEnd, ServiceStartDate)) as Intervaldays
,count(*) as purchases
from cte
where rn <= 3
group by customerId;
Both will not return the expected result unless you subtract purchases (or max(rn)) from Intervaldays. But as you only sum two differences this seems to be not correct for me either...
Additional logic must be applied based on your rules regarding:
customer has less than 3 purchases
overlapping intervals
Assuming there are no overlaps, I think you want this:
select customerId,
sum(datediff(day, ServiceStartDate, ServieEndDate) as Intervaldays
from (select t.*, row_number() over (partition by customerId
order by ServiceStartDate desc) as seqnum
from table t
) t
where seqnum <= 3
group by customerId;
Try this:
SELECT dt.CustomerID,
SUM(DATEDIFF(DAY, dt.PrevExpiry, dt.ServiceStartDate)) As IntervalDays
FROM (
SELECT *
, ROW_NUMBER() OVER (PARTITION BY CustomerID ORDER BY ServiceStartDate DESC) AS rn
, (SELECT Max(ti.ServiceExpiryDate)
FROM yourTable ti
WHERE t.CustomerID = ti.CustomerID
AND ti.ServiceStartDate < t.ServiceStartDate) As PrevExpiry
FROM yourTable t )dt
GROUP BY dt.CustomerID
Result will be:
CustomerId | IntervalDays
-----------+--------------
A | 805
B | 138
I want to return the 4 most recent for each CustomerID. So far I have:
Use Northwind_2012
SELECT CustomerID, OrderDate
FROM Orders
ORDER BY CustomerID;
From here I want to restrict it to the 4 most recent OrderDate's for each CustomerID. Where do I go from here in order to achieve this, as I have also tried using PARTITION BY and ROW_NUMBER but haven't been able to accomplish it yet.
CustomerID OrderDate
ALFKI 2007-08-25 00:00:00.000
ALFKI 2007-10-03 00:00:00.000
ALFKI 2007-10-13 00:00:00.000
ALFKI 2008-01-15 00:00:00.000
ALFKI 2008-03-16 00:00:00.000
ALFKI 2008-04-09 00:00:00.000
ANATR 2008-03-04 00:00:00.000
ANATR 2007-11-28 00:00:00.000
ANATR 2007-08-08 00:00:00.000
ANATR 2006-09-18 00:00:00.000
ANTON 2006-11-27 00:00:00.000
ANTON 2007-04-15 00:00:00.000
You can do this using row_number(), using either a subquery or CTE:
SELECT CustomerID, OrderDate
FROM (SELECT o.*,
ROW_NUMBER() OVER (PARTITION BY CustomerId ORDER BY OrderDate DESC) as seqnum
FROM Orders
) o
WHERE seqnum <= 4
ORDER BY CustomerID;
Tested, works:
(assuming a "OrderID" primary key:
SELECT CustomerId, OrderDate
FROM Orders as ExtOrders
WHERE OrderId IN (
SELECT TOP 4 OrderId
FROM Orders
WHERE CustomerId LIKE ExtOrders.CustomerId
ORDER BY OrderDate DESC)
ORDER BY CustomerId; -- Might not work in SQLFiddle if CustomerId is text
SQLFiddle
Edit, I didn't read the question thoroughly. try this:
Use Northwind_2012
WITH e
AS (
SELECT ROW_NUMBER() OVER (
PARTITION BY CustomerID ORDER BY OrderDate DESC
) AS rownum
,CustomerID
,OrderDate
FROM Orders
)
SELECT CustomerID
,OrderDate
FROM e
WHERE rownum <= 4
http://technet.microsoft.com/en-us/library/ms189461.aspx
SQLFiddle: http://sqlfiddle.com/#!6/afe40/11/0