select last order date for each customer id - sql

I have a list of customerids, orderids and order dates that I want to use in another query to determine if the customer has ordered again since this date.
Example Data:
CustomerID OrderID OrderDate
6619 16034 2012-11-15 10:23:02.603
6858 18482 2013-03-25 11:07:14.680
4784 17897 2013-02-20 14:45:43.640
5522 16188 2012-11-22 14:53:49.840
6803 18016 2013-02-28 10:41:16.713
Query:
SELECT dbo.[Order].CustomerID, dbo.[Order].OrderID, dbo.[Order].OrderDate
FROM dbo.[Order] INNER JOIN
dbo.OrderLine ON dbo.[Order].OrderID = dbo.OrderLine.OrderID
WHERE (dbo.OrderLine.ProductID in (42, 44, 45, 46,47,48))
If you need anything else, just ask.
UPDATE::
This query brings back the results as shown above
Need to know if the customer has ordered again since, for any product id after ordering one of the products in the query above..
Mike

If you are only interested in last order date for each customer
select customerid, max(orderdate) from theTable group by customerid;

In MS SQL you can use TOP 1 for this, you also need to order by your order date column in descending order.
see here SQL Server - How to select the most recent record per user?

SELECT dbo.[Order].CustomerID, MAX(dbo.[Order].OrderDate)
FROM dbo.[Order] INNER JOIN
dbo.OrderLine ON dbo.[Order].OrderID = dbo.OrderLine.OrderID
WHERE (dbo.OrderLine.ProductID in (42, 44, 45, 46,47,48))
GROUP BY dbo.[Order].CustomerID
Gets the latest orderdate of a customer.

ROW_NUMBER in a CTE should work:
WITH cte
AS (SELECT customerid,
orderid,
orderdate,
rn = Row_number()
OVER(
partition BY customerid
ORDER BY orderdate DESC)
FROM dbo.tblorder
WHERE orderdate >= #orderDate
AND customerid = #customerID)
SELECT customerid, orderid, orderdate
FROM cte
WHERE rn = 1
DEMO
(i've omitted the join since no column from the other table was needed, simply add it)

CustomerID and latest OrderDate for customers that have ordered any product after ordering any of a set of products
I suspect they were promotional products
SELECT [Order].[CustomerID], max([Order].[OrderDate])
FROM [Order]
JOIN [Order] as [OrderBase]
ON [OrderBase].[CustomerID] = [Order].[CustomerID]
AND [OrderBase].[OrderDate] < [Order].[OrderDate]
JOIN [OrderLine]
ON [OrderLine].[OrderID] = [OrderBase].[OrderID]
AND [OrderLine].[ProductID] in (42,44,45,46,47,48)
GROUP BY [Order].[CustomerID]

Related

SQL select with GROUP BY and COUNT?

Let's say I have three tables to manage purchases from my online shop:
Products: with columns ID, Name, Price
Customers: with columns ID, Name
Purchases: with columns ProductID, CustomerID, PurchaseDate
Now, how would I go about retrieving products purchased by more than N distinct customers?
I've tried the following on SQL Server 2019 Trial Edition but I'm getting a syntax error on COUNT.
SELECT ProductID, CustomerID, COUNT(*) as C
FROM Purchases
GROUP BY ProductID, CustomerID
HAVING C > 100
ORDER BY C DESC
Better still, how would I go about retrieving products purchased by more than N distinct customers over a 30-day period?
Thanks for any help and/or pointers.
With your current query, you are just counting how often each customer bought each product, because you are grouping by the combination of productid and customerid. Furthermore you cannot reference to the column alias for the count in the HAVING or ORDER BY clause
Try this
declare #purchasedatelower datetime = dateadd(day, -30, getdate())
declare #purchasedateupper datetime = getdate()
declare #distinctcustomers int = 100
select productid, count(distinct customerid) as customercount
from purchases
where purchasedate between #purchasedatelower and #purchasedateupper
group by productid
having count (distinct customerid) >= #distinctcustomers
order by count(distinct customerid) desc
This will return all products, which have been bought by at least 100 distinct customers, in the last 30 days, together with the distinct number of customers.
Try to order them by COUNT()
SELECT ProductID, CustomerID
FROM Purchases
GROUP BY ProductID, CustomerID
HAVING COUNT(*) > 100
ORDER BY COUNT(*) DESC
Now, how would I go about retrieving products purchased by more than N distinct customers?
You would use COUNT(DISTINCT):
SELECT ProductID, COUNT(DISTINCT CustomerID) as num_customers
FROM Purchases
GROUP BY ProductID
HAVING COUNT(DISTINCT CustomerID) > 100
ORDER BY COUNT(DISTINCT CustomerID) DESC;
If you have a particular period in mind, then add a WHERE clause before the GROUP BY.
DECLARE #Popularity int = 1
DECLARE #MonthsAgo int = 1
SELECT p.ProductId, p.ProductName, pc.CustomersDistinct
FROM (
-- Sub-query to compute the aggregate.
-- Count the number of distinct customers that purchased each product.
SELECT ProductId, COUNT(DISTINCT CustomerId) AS CustomersDistinct
FROM Purchases
WHERE PurchaseDate >= DATEADD(MONTH, -#MonthsAgo, CURRENT_TIMESTAMP)
GROUP BY ProductId
HAVING COUNT(DISTINCT CustomerId) > #Popularity
) AS pc
-- Now do JOINs to look up helpful values for the final SELECT list.
INNER JOIN Products p ON pc.ProductId = p.ProductId
ORDER BY 3 DESC

Counting table rows where a column value first appears after date XYZ

We have an orders table that looks like so:
orderId
customerId
orderDate
320
45
2020-01-01
455
67
2021-02-11
122
45
2019-04-22
Based on this I need to count all 'new' customers that first entered the system after date XYZ.
I'm thinking of something involving a having clause but wondered if there was a better way to go about it. Something along these lines (SQL may not be exact, but the general idea):
select count(*) from (select distinct(customerId) from orders group by customerId having min(orders.orderDate) > XYZ) as foo
Is there a better / faster way to go about this?
Assuming you wanted the count of new customers coming into the system after 2021-02-11, you could try:
SELECT COUNT(DISTINCT customerId)
FROM orders o1
WHERE
orderDate > '2021-02-11' AND
NOT EXISTS (SELECT 1 FROM orders o2
WHERE o2.customerId = o1.customerId AND o2.orderDate <= '2021-02-11');
The above logic reads, in plain English, to count any customer record appearing after 2021-02-11, where that customer also did not appear previously in a record on or before 2021-02-11.
Your query is already fine, another option is to use partition and count only 1 customerId (alternative of distinct keyword)
select count(1) from (select
row_number() over (partition by customerId order by orderDate asc) rn
from orders where orderDate > '2020-01-01') t1
where rn = 1
Try dbfiddle
You are already using this method:
select count(*)
from (select customerId, min(orderDate) as first_orderDate
from orders o
group by customerId
having min(orderDate) >= '2021-02-11'
) oc;
For performance, I would suggest using the customers table:
select count(*)
from customers c
where not exists (select 1
from orders o
where o.customerId = c.customerId and
o.orderDate < '2021-02-11'
);
For this, you want an index on orders(customerId, orderDate).

Max function on date giving multiple records

I have got 3 tables: order, customer and invoice. I need to get the latest invoice number for each customer.
I am using the max function on order date and then grouping by customer number and invoice number, where order status was confirmed or shipped.
select max(o.order_date), c.customer_number, i.invoice_number
from orders o , invoices i , customer c
where o.order_oid = i.order_oid
and c.customer_oid = i.customer_oid
and o.status_oid in ( 4,6)
group by c.customer_number, i.invoice_number;
I am getting duplicate records like:
Date cust_num invc#
1/22/2018 479 I128
4/23/2018 479 I287
5/18/2018 479 I433
It should have returned me only last record. What am I doing wrong?
From your description and comments you seem to want:
select max(o.order_date), c.customer_number,
max(i.invoice_number) keep (dense_rank last order by o.order_date) as invoice_number
from orders o , invoices i , customer c
where o.order_oid = i.order_oid
and c.customer_oid = i.customer_oid
and o.status_oid in ( 4,6)
group by c.customer_number;
The group by no longer includes the invoice number; instead the last invoice number, based on date, is found using last.
If the invoice numbers are strictly in date order, and fixed length, you could potentially just do:
select max(o.order_date), c.customer_number, max(i.invoice_number) as invoice_number
but if if it's possible to go from say invoice I999 to I1000 then it isn't safe to sort those just as strings, since - as a string 'I1000' will sort before 'I999'.
Not related, but you might want to consider moving to modern join syntax:
select max(o.order_date), c.customer_number,
max(i.invoice_number) keep (dense_rank last order by o.order_date) as invoice_number
from orders o
join invoices i on i.order_oid = o.order_oid
join customer c on c.customer_oid = i.customer_oid
where o.status_oid in (4, 6)
group by c.customer_number;
You need max(invoice_number) to avoid getting a record per invoice
You can use row_number() window analytic function
select order_date, customer_number, invoice_number
from
(
select o.order_date, c.customer_number, i.invoice_number,
row_number() over (partition by c.customer_number order by o.order_date desc) as rn
from orders o
join invoices i on o.order_oid = i.order_oid
join customer c on c.customer_oid = i.customer_oid
where o.status_oid in (4,6)
)
where rn = 1;
P.S. : Of course, proper to give up old-style comma seperated join for the queries

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]

Sql query to select records for a single distinct customer id

I have written a query for sales by customers in groups it is as follows:
SELECT customerid,
SUM (salestax1)As total_salestax1,
SUM(total_payment_received) As total_payment_recieved,
COUNT (orderid)as order_qty,
SUM(paymentamount)As paymentamount
FROM Orders_74V94W6D22$
WHERE orderdate between '7/6/2011 16:35' and '2/3/2012 11:53'
GROUP BY customerid
but this query shows only 5 fields but I need to show following fields:
orderid billingcompanyname billingfirstname billinglastname
billingcountry shipcountry paymentamount creditcardtransactionid
orderdate creditcardauthorizationdate orderstatus
total_payment_received tax1_title salestax1
then how to deal with it?
you need to understand what GROUP BY means.
If you are grouping by customerId, you will have only one customer because all data is grouped into it.
How do you want to group by orderid and display the orderid on your result set? If you have 10 order ids, do you expect 10 rows on the result? If yes, fine, group by it but I don't think that's what you want
EDIT:
Well, this is NOT a good idea, your table structure is WRONG and I dont think you fully understand that a group by means, BUT I think this query will get your result:
SELECT customerid,
(select top 1 [column1] from Orders_74V94W6D22$ where customerid = ORD.customerid),
(select top 1 [column2] from Orders_74V94W6D22$ where customerid = ORD.customerid),
(select top 1 [column3] from Orders_74V94W6D22$ where customerid = ORD.customerid),
SUM (salestax1)As total_salestax1,
SUM(total_payment_received) As total_payment_recieved,
COUNT (orderid)as order_qty,
SUM(paymentamount)As paymentamount
FROM Orders_74V94W6D22$ ORD
WHERE orderdate between '7/6/2011 16:35' and '2/3/2012 11:53'
GROUP BY customerid
To select more about the customer, you need to use your query as a sub query, something like:
Select distinct c.[column1], c.[column2], c.[column3], tbl.*
From Orders_74V94W6D22$ c inner join (
SELECT customerid,
SUM (salestax1)As total_salestax1,
SUM(total_payment_received) As total_payment_recieved,
COUNT (orderid)as order_qty,
SUM(paymentamount)As paymentamount
FROM Orders_74V94W6D22$
WHERE orderdate between '7/6/2011 16:35' and '2/3/2012 11:53'
GROUP BY customerid
) as tbl on tbl.customerid = c.customerid
but you cant logically select something about 1 order as youve grouped multiple orders