Select 3 lastest order for each customer - sql-server-2005

Here is my table CusOrder that collect customer order
OrderID Cus_ID Product_ID NumberOrder OrderDate
1 0000000001 9 1 6/5/2553 0:00:00
2 0000000001 10 1 6/5/2553 0:00:00
3 0000000004 9 2 13/4/2553 0:00:00
4 0000000004 9 1 17/3/2553 0:00:00
5 0000000002 9 1 22/1/2553 0:00:00
7 0000000005 9 1 16/12/2552 0:00:00
8 0000000003 9 3 13/12/2552 0:00:00
10 0000000001 9 2 19/11/2552 0:00:00
11 0000000003 9 2 10/11/2552 0:00:00
12 0000000002 9 1 23/11/2552 0:00:00
I need to select 3 lastest order for each customer and I need all customer
so it will show each customer and his/her 3 lastest order
how can I do it
sorry for my bad english

This CTE should work for you:
;with cteTop3PerGroup as
(
select row_number() over(partition by Cus_ID order by OrderDate DESC) as RN
, *
from CusOrder
)
select * from cteTop3PerGroup
where RN <= 3

WITH Temp AS
(
SELECT *, (ROW_NUMBER() OVER (PARTITION BY Cus_ID ORDER BY OrderDate DESC)) AS Number
FROM CusOrder
)
SELECT * FROM Temp WHERE Number <= 3
Should work. Not tested with this exact database structure, but something similar.

"Pure" SQL solution.
With Customers As (
Select Cus_ID From test Group By Cus_ID
),
TopLastOrders as (
Select o.Cus_ID, Max(OrderDate) as OrderDate
From test o Inner Join Customers c on c.Cus_ID = o.Cus_ID
Group By o.Cus_ID
),
TopSecondOrders as (
Select o.Cus_ID, Max(OrderDate) as OrderDate
From test o Inner Join Customers c on c.Cus_ID = o.Cus_ID
Where Not Exists (Select 1 From TopLastOrders Where Cus_ID=o.Cus_ID And OrderDate=o.OrderDate)
Group By o.Cus_ID
),
TopThirdOrders as (
Select o.Cus_ID, Max(OrderDate) as OrderDate
From test o Inner Join Customers c on c.Cus_ID = o.Cus_ID
Where Not Exists (Select 1 From TopLastOrders Where Cus_ID=o.Cus_ID And OrderDate=o.OrderDate)
And Not Exists (Select 1 From TopSecondOrders Where Cus_ID=o.Cus_ID And OrderDate=o.OrderDate)
Group By o.Cus_ID
)
Select
c.Cus_ID,
t1.OrderDate,
t2.OrderDate,
t3.OrderDate
From
Customers c
Left Outer Join TopLastOrders t1 on t1.Cus_ID = c.Cus_ID
Left Outer Join TopLastOrders t2 on t2.Cus_ID = c.Cus_ID
Left Outer Join TopLastOrders t3 on t3.Cus_ID = c.Cus_ID
Order By
c.Cus_ID
I'm not sure what output you need but this should not be hard.

Related

How to show the count of all items in cross joined table in SQL Server

I have a table that has all Items in the inventory, table called CI
CI has 2 columns (ProdID and Price), and it looks like this
ProdID Price
-------------
A8373 700
G8745 900
J7363 300
K7222 800
Y6311 350
I have another table for documents called Docs with columns DocID, CustID and InvoiceID.
DocID, CustID, InvoiceID
------------------------
1 1001 751
2 1001 752
3 1001 753
4 1002 831
5 1002 832
6 1003 901
7 1003 902
Another table for purchases called Purchase with DocID, ProdID, ProdSize.
In the same invoice, ProdID can be repeated as it can be in different sizes
DocID, ProdID, ProdSize
------------------------
1 A8373 41
1 A8373 42
1 A8373 43
1 G8745 35
1 G8745 36
2 A8373 44
2 A8373 45
Now I want to get the quantity of of products for all customer and invoice, but for highest priced products
So it should be like this
CustID, InvoiceID, ProdID, Quantity
-----------------------------------
1001 751 A8373 3
1001 751 G8745 2
1001 751 K7222 0
1001 752 A8373 2
1001 752 G8745 0
1001 752 K7222 0
and to show 0 for the products that do not exist in that invoice
I wrote this query, but it is extremely slow. I wonder if there is an easier fast way to get this results
DECLARE #Features AS TABLE
(
CustID varchar(100),
InvoiceID varchar(100)
INDEX IX3 CLUSTERED(CustID, InvoiceID),
ProdID varchar(100),
Quantity bigint
)
INSERT INTO #Features (CustID, InvoiceID, ProdID, Quantity)
SELECT
R.CustID, R.InvoiceID, T.ProdID, COUNT(*) AS Quantity
FROM
Docs R
CROSS JOIN
(SELECT TOP 1000 * FROM CIs ORDER BY Price DESC) C
INNER JOIN
Purchase T ON T.DocID = R.DocID
GROUP BY
R.CustID, R.InvoiceID, T.ProdID
SELECT TOP 100 *
FROM #Features
ORDER BY CustID, InvoiceID, ProdID
SELECT COUNT(*) FROM #Features
UPDATE F
SET Quantity = Cnt
FROM #Features F
INNER JOIN
(SELECT R.CustID, R.InvoiceID, COUNT(*) Cnt
FROM Purchase T
INNER JOIN Docs R ON T.DocID = R.DocID
GROUP BY R.CustID, R.InvoiceID ) X ON F.CustID = X.CustID
AND F.InvoiceID = X.InvoiceID
SELECT * FROM #Features
here is a way to do this. I filter out the 1000 products first and then perform the join as follows..
Also there isn't a need for update query, all could be obtained in the SQL itself.
Filter early join late
with top_product
as (select prodid,price, rownumber() over(order by price desc) as rnk
from ci
)
,invoice_product
as(select d.docid,d.custid,d.invoiceid,p.prodid
from top_product
join docs d
on 1=1
and rnk<=1000
)
select a.CustID, a.InvoiceID, a.ProdID,count(b.prodid) as qty
from invoice_product a
left join purchase b
on a.DocID=b.docid
and a.ProdID=b.prodid
group by a.CustID, a.InvoiceID, a.ProdID
You can use the DENSE_RANK as follows:
select CustID, InvoiceID, ProdID, sum(qty) as qty
from (select d.CustID, d.InvoiceID, ci.ProdID, p.prodid as qty,
dense_rank() over (order by ci.price desc) as rn
from ci cross join docs d
left join purchase p on d.docid = p.docid and ci.prodid = p.prodid) t
where rn <= 1000
group by CustID, InvoiceID, ProdID
Can you please try following SQL Select statement where I used Common Table Expression SQL CTEs
with topproducts as (
select top 3 ProdID from CI order by Price desc
), sales as (
select
CustID,
InvoiceID,
ProdId,
count(ProdId) as cnt
from (
select
d.CustID,
d.InvoiceID,
p.ProdId
from Docs d
inner join Purchase p
on p.DocID = d.DocID
where p.ProdId in (select ProdId from topproducts)
) t1
group by
CustID,
InvoiceID,
ProdId
)
select
t.*, isnull(ss.cnt,0) as Qty
from (
select
distinct s.CustID, s.InvoiceID, p.ProdId
from sales s, topproducts p
) t
left join sales ss on ss.InvoiceID = t.InvoiceID and ss.ProdId = t.ProdId

How to use distinct and count with partition by

I want rows with distinct agt_id along with the count. Following is the query i am currently using but need help to get distinct rows.
with cust as
(
SELECT customer_id, cnic
FROM customer
where customer_id
not in
(select agent_id from agent
where to_date(created_on) BETWEEN '2020-06-01' AND '2020-06-30')
)
select agt.agent_id, c.customer_id, c.cnic, agt.transaction_type_id,
agt.transaction_type_name , row_number() OVER(PARTITION BY c.customer_id) AS agent_count
from cust as c
INNER JOIN konnect_ag_transaction_vw agt ON c.cnic= agt.receiver_cnic
where
agt.status ='PROCESSED'
AND agt.transaction_type_id IN (1,2,3)
Current Output using above query:
agt_id cus_id Count
1 89563 93587 7
2 89563 93587 7
3 89563 93587 7
4 89563 93587 7
5 89563 93587 7
6 56139 93587 7
7 56139 93587 7
Count in the above output is the total count of rows with same cus_id where as i want count of agt_id link with same cus_id
Desired output:
agt_id cus_id Count
1 89563 93587 2
2 56139 93587 2
If I understand correctly you need a simple group by with count()
with cust as
(
SELECT customer_id, cnic
FROM konnect_bb_customer_vw
where customer_id
not in
(select agent_id from konnect_bb_agent_h_vw
where to_date(created_on) BETWEEN '2020-06-01' AND '2020-06-30')
)
select agt.agent_id, c.customer_id, count(*)
from cust as c
INNER JOIN konnect_ag_transaction_vw agt ON c.cnic= agt.receiver_cnic
where agt.status ='PROCESSED' AND agt.transaction_type_id IN (1,2,3)
group by agt.agent_id, c.customer_id
I suspect that you want aggregation:
select agt.agent_id, c.customer_id, count(*)
from cust c join
konnect_ag_transaction_vw agt
on c.cnic = agt.receiver_cnic
where agt.status = 'PROCESSED' and
agt.transaction_type_id in (1, 2, 3)
group by agt.agent_id, c.customer_id;
using DISTINCT keyword as such
select DISTINCT agt.agent_id, c.customer_id, c.cnic, agt.transaction_type_id,
agt.transaction_type_name , row_number() OVER(PARTITION BY c.customer_id) AS agent_count
from cust as c
INNER JOIN konnect_ag_transaction_vw agt ON c.cnic= agt.receiver_cnic
where
agt.status ='PROCESSED'
AND agt.transaction_type_id IN (1,2,3)

How to Sum previos rows summatory to find a balance in SQL Server

I'm working on a service that needs to calculate how much a customer owes, according to a total invoice value and the partial payments that the customer has made.
So, in a tableA I have a row with the invoice total value:
[dbo].[TableA]
ID CustomerId InvoiceVal
1 12 1000
2 11 2000
3 10 5000
4 14 15000
5 12 100
6 16 8000
7 18 3200
In a TableB I have the record of each customer's partial payments they have made to each invoice:
[dbo].[TableB]
ID InvoiceId Payment
1 1 150
2 3 50
3 1 120
4 1 100
5 5 90
6 4 7500
So, as you can see, the customer 12 has an invoice for $1000 and has made 3 payment that sum $370
I need to be able to se the partial total owed in each row, this is the expected result:
No. InoviceId CustomerId Payment Owed
1 1 12 150 850
2 1 12 120 730
3 1 12 100 630
So far, this is my code:
DECLARE #invid int = '1'
DECLARE #invoicetotal numeric(18,2)
SET #invoicetotal =
(
SELECT
[dbo].[TableA].[InvoiceVal]
FROM [dbo].[TableA]
WHERE
([dbo].[TableA].[ID] = #invid)
)
SELECT
*,
SUM(#invoicetotal - [dbo].[TableB].[Payment]) OVER(ORDER BY [dbo].[TableB].[ID] ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING) AS [Owed]
FROM [dbo].[TableB]
WHERE
([dbo].[TableB].[InvoiceId] = #invid)
But this is what I get:
ID InvoiceId Payment Owed
1 1 150.00 NULL
3 1 120.00 850.00
4 1 100.00 1730.00
I need to sum the previous payment on each row.
Thanks!
Something like this would help
SELECT
ROW_NUMBER() OVER (ORDER BY TableB.ID ASC) NO,
CustomerId,
Payment,
InvoiceVal - SUM(Payment) OVER (PARTITION BY TableA.ID ORDER BY TableB.Id ASC) Owed
FROM TableA
INNER JOIN TableB
ON TableA.Id = TableB.InvoiceId
WHERE
CustomerId = 12
Working Fiddle
Your query will depend on what you are trying to make as your final output.
If you want just ONE CustomerID, go with:
SELECT a.ID AS InvoiceID, a.CustomerID, a.InvoiceVal AS StartInvoice
, b.ID AS bid, b.Payment
, a.InvoiceVal - ISNULL(SUM(b.Payment) OVER (PARTITION BY a.ID ORDER BY b.id),0) AS owed
FROM TableA a
LEFT OUTER JOIN TableB b ON a.ID = b.InvoiceID
WHERE a.CustomerID = 12
And if that CustomerID doesn't have any payments, you want to use a LEFT JOIN so that you don't eliminate an amount owed.
SELECT a.ID AS aid, a.CustomerID, a.InvoiceVal AS StartInvoice
, b.ID AS bid, b.Payment
, a.InvoiceVal - ISNULL(SUM(b.Payment) OVER (PARTITION BY a.ID ORDER BY b.id),0) AS owed
FROM TableA a
LEFT OUTER JOIN TableB b ON a.ID = b.InvoiceID
WHERE a.CustomerID = 11
I also added an ISNULL() around Payment to keep from nulling out your owed amount. It could also be added to the InvoiceVal to account for a CustomerID who hasn't been invoiced yet, if that was needed (or possible from other tables).
IF you want to get ALL CustomerIDs, you'll have to account for that in your partition.
SELECT s1.CustomerID, aid AS InvoiceID, s1.bid, s1.Payment
, (s1.StartInvoice - s1.runningPayment) AS Owed
FROM (
SELECT a.ID AS aid, a.CustomerID, a.InvoiceVal AS StartInvoice
, b.ID AS bid, b.Payment
, ISNULL(SUM(b.Payment) OVER (PARTITION BY a.CustomerID, a.ID ORDER BY b.id),0) AS runningPayment
FROM TableA a
LEFT OUTER JOIN TableB b ON a.ID = b.InvoiceID
) s1
ORDER BY s1.CustomerID, s1.aid, s1.bid
Fiddle demonstrates overpayment or paying total balance for 0 owed.

Calculation of points if there are 2 student with same rank in sql server

Rank_Table
ID Rank
1 1
2 1
3 3
4 3
5 5
Price
No Points
1 10
2 9
3 8
4 7
5 6
Expected Output
ID Rank Points
1 1 9.5
2 1 9.5
3 3 7.5
4 3 7.5
5 5 6
2nd rank is not present so 1st and 2nd points are sum together and distributed among the number of the student
for eg : (10+9) / 2 = 9.5
When I join the 2 table like
select *
from Rank_table a join
Price b
on a.ID = b.No
I am getting the output as
ID Rank No Points
1 1 1 10
2 1 2 9
3 3 3 8
4 3 4 7
5 5 5 6
This seems to be quite a simple requirement, simply using AVG and an OVER clause.
CREATE TABLE [Rank] (ID int, [Rank] int)
CREATE TABLE Price ([No] int, Points int);
GO
INSERT INTO [Rank]
VALUES
(1,1),
(2,1),
(3,3),
(4,3),
(5,5);
INSERT INTO Price
VALUES
(1,10),
(2,9),
(3,8),
(4,7),
(5,6);
GO
SELECT R.ID,
R.[Rank],
AVG(CONVERT(decimal(2,0),P.Points)) OVER (PARTITION BY R.[Rank]) AS Points
FROM [Rank] R
JOIN Price P ON R.ID = P.[No];
GO
DROP TABLE [Rank];
DROP TABLE Price;
You need simple JOIN :
select rn.*,
avg(convert(decimal(10,0), p.Points)) over (partition by rn.rnk) as points
from Rank_Table rn inner join
Price p
on p.id = rn.No;
SELECT *,
AA.pts AS POINTS
FROM rank_table R
INNER JOIN (SELECT rank,
Sum(points) / Count(*) AS PTS
FROM rank_table a
JOIN price b
ON a.id = b.no
GROUP BY rank)AA
ON ( R.rank = AA.rank )
You can calculate AVG at rank level and then join back to Rank_Table like in this working demo
select R.*,T.points from Rank_table R
JOIN
(
select rank, points=avg(cast(points as decimal(10,2)))
from Rank_table a join
Price b
on a.ID = b.No
group by rank
)T
on T.rank=R.rank
Hmmm. You seem to want a non-equijoin based on the "next" rank as well as the rank in each row. Unfortunately, SQL Server 2008 doesn't support lead(), but you can use apply:
select rt.id, rt.rank, avg(p.price * 1.0) as points
from rank_table rt outer apply
(select min(rt2.rank) as next_rank
from rank_table rt2
where rt2.rank > rt.rank
) rt2 left join
price p
on p.no >= rt.rank >= p.no and
(p.no < rt2.next_rank or rt2.next_rank is null)
group by rt.id, rt.rank;

SQL Server 2005 MAX, SUM AND GROUP BY

I am using SQL Server 2005 and I have a problem with my SQL query. Basically, I want to get the total amount of all transactions of customers, grouped by company, based on the latest dates from all customer transactions.
Sample data:
Customer_Id Date Amount COMPANY
-------------------------------------------------
1 3/3/2014 9021 COMPANY X
2 3/3/2014 12000 COMPANY Y
2 3/15/2014 10000 COMPANY Y
2 3/30/2014 8000 COMPANY Y
4 3/13/2014 10000 COMPANY Z
5 3/14/2014 1400 COMPANY X
1 3/16/2014 2500 COMPANY X
7 3/14/2014 110 COMPANY Y
3 3/17/2014 1500 COMPANY Z
2 3/19/2014 2044 COMPANY Y
3 3/09/2014 9400 COMPANY Z
3 3/11/2014 8950 COMPANY Z
2 3/31/2014 3455 COMPANY Y
3 3/15/2014 950 COMPANY Z
6 3/15/2014 5543 COMPANY X
What I want to accomplish is like this:
COMPANY TOTAL
COMPANY X 9443 --> sum from customer_id 1 (2500, as of 3/16/2014) and customer_id 6 (5542, 3/15/2014) and customer_id 5 (1400 as of 3/14/2014)
COMPANY Y 3455 --> sum from customer_id 2 (3455, as of 3/31/2014)
COMPANY Z 10950 --> sum from customer_id 4 (1000, as of 3/13/2014) and customer_id 3 (950, as of 3/15/2014)
Below are some SQL queries I've tried which doesn't work on my goal:
SELECT TOP (1) WITH TIES
Date, Company, SUM(Amount) AS total
FROM
tbl_Table
GROUP BY
Date, Company
ORDER BY
Date DESC
SELECT
t1.Date, t1.Company, SUM(t1.Amount) AS total
FROM
tbl_Table AS t1
INNER JOIN
(SELECT
MAX(Date) AS date, Company
FROM
tbl_Table
GROUP BY
Company) AS t2 ON t1.Date = t2.Date AND t1.Company = t2.Company
GROUP BY
t1.Date, t1.Company
WITH latest AS
(SELECT
Company, MAX(Date) AS maxdate
FROM
tbl_Table
GROUP BY
Company
)
SELECT
a.Date, a.Company, SUM(a.Amount) AS total
FROM
tbl_Table AS a
INNER JOIN
latest AS b ON a.Company = b.Company AND a.Date = b.maxdate
GROUP BY
a.Date, a.Company
Your results are still not correct by data you give.
SQLFIDDLEExample
Query:
SELECT t1.Company,
SUM(t1.Amount) Total
FROM Table1 t1
LEFT JOIN Table1 t2
ON t1.COMPANY = t2.COMPANY
AND t1.Customer_Id = t2.Customer_Id
AND t1.Date < t2.Date
WHERE t2.Customer_Id is null
GROUP BY t1.Company
Result:
| COMPANY | TOTAL |
|-----------|-------|
| COMPANY X | 9443 |
| COMPANY Y | 3565 |
| COMPANY Z | 11500 |
Try This
WITH cte AS (
SELECT Amount,Company,
ROW_NUMBER() OVER (PARTITION BY Customer_Id ORDER BY CAST([Date] AS DATETIME) desc )
AS dateRowRank
)
SELECT Company,SUM(Amount)
FROM cte
WHERE dateRowRank=1
GROUP BY Company