Average of last X orders - sql

I need to find the average amount of the last X orders, per customer. My data is structured as such:
Customer ID
Total Amount
Date
I tried partitionning by Customer ID, then Ordering by date, but I can't find the average of the TOP X.

Another option is a cte and Row_Number(). The following will give you the average order by customer.
Declare #YourTable table (CustID int,OrderDate Date,OrderAmount int)
Insert Into #YourTable values
(1,'2016-01-18',2500),
(1,'2016-02-13',5000),
(1,'2016-03-31',3000),
(2,'2016-03-18',1800),
(2,'2016-04-13',2200),
(2,'2016-05-31',2500)
;with cteBase as (
Select *
,RowNr=Row_Number() over (Partition By CustID Order By OrderDate Desc)
From #YourTable
)
Select CustID
,AvgOrder = avg(OrderAmount)
From cteBase
Where RowNr<=2
Group By CustID
Returns
CustID AvgOrder
1 4000
2 2350

Use ROW_NUMBER window function
select [Customer ID],Avg([Total Amount]) as avg_amt
(
select row_number()over(partition by [Customer ID] order by [Date] desc) rn, *
from yourtable
) A
Where Rn <= 5 --change it based on requirement
Group by [Customer ID]
Looks like current table is transaction table, so you may have a separate table called Customer where [Customer ID] is unique then you can use this approach also
SELECT c.[Customer ID],
Avg([Total Amount])
FROM customers c
CROSS apply (SELECT TOP 5 [Total Amount]
FROM yourtable y
WHERE c.[Customer ID] = y.[Customer ID]
ORDER BY [Date] DESC) cs
GROUP BY [Customer ID]
The above query can be altered with OUTER APPLY to get list of all customers even though he did not make any transaction with the avg Total Amount zero.

One method uses row_number():
select customerid, avg(totalamount)
from (select t.*,
row_number() over (partition by customerid order by date desc) as seqnum
from t
) t
where seqnum <= x;

Related

Sql: One material code, having a particular vendor code, having multiple rates

I need to have a count of the Material codes having multiple rates for a particular Vendor code
The example
I need the Requiredoutput to be
11005433 1000323 15
11005433 1000323 0
You can use this.
DECLARE #T TABLE (Material VARCHAR(10), [Vendor code] VARCHAR(10), rates INT)
INSERT INTO #T
VALUES
('11005432','1000321',15),
('11005433','1000323',15),
('11005433','1000323',0),
('11005433','1000324',25)
;WITH CTE AS (
SELECT *, RN = ROW_NUMBER() OVER (PARTITION BY Material, [Vendor code] ORDER BY Material) FROM #T
)
SELECT T.* FROM CTE
INNER JOIN #T T ON CTE.Material = T.Material AND CTE.[Vendor code] = T.[Vendor code]
WHERE CTE.RN> 1
Result:
Material Vendor code rates
---------- ----------- -----------
11005433 1000323 15
11005433 1000323 0

SQL Query to return data based on last date recorded by item and by Client

From the attached data sample I need to create an sql query in order to return the latest transaction for that account and that particular code. i.e the query should return as follows ;
The last price of 60.00 was recorded for account S494 and code 5044 on the 24/10/2016. This will identify when last the client bought this item and at which price.
SELECT code,
account,
price,
trandate
FROM
(SELECT t.*,
row_number() over (partition BY account, code order by convert(datetime,trandate,103) DESC) rn
FROM your_table
) t
WHERE rn = 1;
You can use RANK() to partition and select the range you require (in this case you need the top 1, so where RANK() is 1 - but you can amend the where clause accordingly).
You could just use ROW_NUMBER() instead, but you might have multiple records where trandate is the same, so you need both records where RANK() is 1 (using the data in your example - if this will never be the case use ROW_NUMBER).
SELECT [code], [account], [price], [trandate]
FROM
( SELECT RANK() OVER (PARTITION BY [account], [code] ORDER BY [trandate] DESC) [rank], [code], [account], [price], [trandate] FROM [#trans] ) [d]
WHERE [d].[rank] = 1
select TOP 1 CODE,ACCOUNT,PRICE,TRANSDATE
FROM TABLE
ORDER BY CAST(TRANSDATE AS DATE) DESC

How to return most recent record

I have two tables, Sales and SalesNotes, as below
Sales
SO No......Cust.........Date
1..........Me..........22-04-13
2..........You.........23-04-13
SalesNotes
SO No.......Note.......Notedate
1...........Blah.......24-04-13
2...........Bleh.......23-04-13
2...........Bluh.......27-04-13
How can I return a result set showing Cust, date and the most recent dated note for the corresponding SO no?
I have tried using MAX() but cannot use an aggregate in the where clause and do not understand how I could implement HAVING to do what I need.
What I am trying to achieve is:
SO No.......Cust........Note
1...........Me..........Blah
2...........You.........Bluh
One way of doing this is with the row_number window function:
SELECT s.[So no], [cust], [Note]
FROM [Sales] s
LEFT JOIN (SELECT [So no], [Note],
ROW_NUMBER() OVER (PARTITION BY [So no]
ORDER BY [Notedate] DESC) rn
FROM [SalesNotes]) n ON s.[So no] = n.[So no] AND rn = 1
You can use outer apply for this:
select s.*, sn.note
from sales s outer apply
(select top 1 sn.*
from salesnotes sn
where sn.so_no = s.so_no
order by sn.notedate desc
) sn;
You can use window function FIRST_VALUE to get the most recent Note value per [SO No.]:
SELECT s.[SO No.], Cust, Note
FROM Sales AS s
INNER JOIN (SELECT DISTINCT [SO No.],
FIRST_VALUE(Note) OVER (PARTITION BY [SO No.]
ORDER BY NoteDate DESC) AS Note
FROM SalesNotes) AS sn
ON s.[SO No.] = sn.[SO No.]
This way you avoid using a correlated subquery, which, I think, is less performant.
FIRST_VALUE is available from SQL Server 2012+.
Something like this:
select s.so_no, s.cust,
(select top (1) n.note from salesnotes n where s.so_no = n.so_no order by notedate desc) as note
from sales s
I understand your output as last sale and last note for that sale. If you want to do this for all sales, just remove first Top 1.You can do it with Apply:
Select Top 1 s.SoNo, s.Cust, oa.Note
From Sales s
Outer Apply (Select Top 1 Note From Notes n Where n.SoNo = s.SoNo Order By Date Desc) oa
Order By s.Date desc
Since you were talking about 'max' in where clause , this is just for you to understand 'max' . And just so you know this is not a good solution because there are number of problems like what if notes are same for two different customer, what if customers have the same name and so on and so forth. You should follow the outer apply concept as others have suggested for the right solution .
Select s.SoNo, s.cust, sn.notes, max(sn.noteDate) from sales s inner join salesnotes sn on s.sono=sn.sono group by s.sono,s.cust,sn.notes

Using max function without grouping

I have three sets of information:
productID
date
seller
I need to get information of last product sold for each productID and sold by. I tried using max value of date but that forces me to use grouping for seller as well but I don't want to group by seller. I want to group by productID, get the date it was sold last and by who. How can I avoid grouping on seller?
Use Window function which will help you to find the Latest date in each group(productId)
SELECT ProductID,
[date],
seller
FROM (SELECT Row_number()
OVER(
partition BY ProductID
ORDER BY [date] desc) Rn,
*
FROM tablename) a
WHERE rn = 1
or use can also use Max aggregate with group by to get the result
SELECT ProductID,
[date],
seller
FROM tablename a
JOIN (SELECT Max([date]) [date],
productid
FROM tablename
group by productid) b
ON a.productid = b.productid
AND a.[date] = b.[date]

Get each patients' highest bill

I have a table where an ID can be associated with more than one bill. What I need to do is find the MAX billing amount, the ID and the date of their highest (MAX) bill. The problem is that there can be thousands of billsper person, and hundreds on any given date.
My query
select patientID, max(amountPaid) as maxPaid
from myTable
group by patientID
gives me what I need, minus the date. My attempt at fixing this is
select t.patientID, t.maxPaid, myTable.billDate
from myTable
inner join
(
select patientid, max(amountPaid) as maxPaid
from myTable
group by patientID
) as t on t.patientID=myTable.patientID and =t.maxPaid=myTable.maxPaid
The error given is invalid column name maxPaid. I tried not giving the calculated field an alias but SQL Server wouldn't accept myTable.max(amountPaid) either. What's the quickest way to fix this? thanks in advance.
The problem with your current approach is that if a patient has two bills with the maximum amount, you will get both of them.
Try this instead:
SELECT
patientid,
amountPaid AS max_paid,
billDate
FROM
(
SELECT
patientid,
amountPaid,
billDate,
ROW_NUMBER() OVER (PARTITION BY patientid
ORDER BY amountpaid DESC) AS RowNumber
FROM myTable
) T1
WHERE T1.RowNumber = 1
This will always return one row per patient even if a patient has two bills that both have the same maximum amountpaid.
;WITH x AS (SELECT PatientID, BillDate, AmountPaid,
rn = ROW_NUMBER() OVER (PARTITION BY PatientID ORDER BY AmountPaid DESC)
FROM dbo.myTable
)
SELECT PatientID, BillDate, AmountPaid
FROM x
WHERE rn = 1;
Based on you description, I think you meant this:
select t1.patientID, t2.maxPaid, t1.billDate
from myTable t1
inner join
(
select patientid, max(amountPaid) as maxPaid
from myTable
group by patientID
) t2
on t1.patientID=t2.patientID
and t1.amountPaid=t2.maxPaid