I'm trying to query my SQL database to get the number of orders made by each client within a certain date range.
I have a list of orders as follows
CustomerName ClientID DateOrdered
Customer No.2 10 2011-11-25
Customer No.3 11 2011-10-15
Customer No.3 11 2011-11-25
and I want to be able to find out how many orders have been made by a specific client for example between 2011-11-1 and 2011-11-30, this should result in :
CustomerName ClientID Number
Customer No.3 11 1
Customer No.2 10 1
So far I've managed to get this
SELECT CustomerName, ClientID, COUNT(*) AS Number
FROM Orders t
GROUP BY CustomerName, ClientID
HAVING COUNT(*) =
(SELECT MAX(Number)
FROM
(SELECT CustomerName, ClientID, COUNT(*) AS Number
FROM Orders
GROUP BY CustomerName, ClientID ) x
WHERE CustomerName = t.CustomerName )
Which gives me every order the customer has ever made
CustomerName ClientID Number
Customer No.3 11 2
Customer No.2 10 1
Am I going about the right way to solve this or is there a simpler way which I've completely overlooked!
Should work fine:
select CustomerName, ClientID, count(*) as Number
from Orders
where DateOrdered between '20111101' and '20111130'
group by CustomerName, ClientID
select CustomerName, ClientID, count(*)
from
(
select CustomerName, ClientID
from Orders
where datediff(mm, DateOrdered, getdate()) <= 1
)a
group by CustomerName, ClientID
What this does is utilize a subquery that filters the rows by the dates in a given month (that seems to be what you are looking for). Then it groups by the CustomerName and ClientID and gets the sum of their orders.
select ClientID, max(CustomerName), count(*)
from Orders
where DateOrdered between '2011-11-01' and '2011-11-30'
group by ClientID
Depending on database, the syntax of the DateOrdered selection may need to be varied.
SELECT
c.CustomerName,
count(o.OrderID) as Orders
FROM
orders o
JOIN clients c ON o.ClientID = c.ClientID
WHERE
o.DateOrdered BETWEEN '2011-11-01' AND '2011-11-20'
GROUP BY c.ClientID ;
Related
Hello I am trying to group multiple customer orders into buckets in SQL, the output should look something like it does below. Do I have to use a case statement to group them?
Table1 looks like:
CustomerID
Order_date
1
somedate
2
somedate
3
somedate
2
somedate
Edit: # of customers meaning if CustomerID 2 had 2 orders he/she would be of the in the bucket of #of orders of 2.
Output should be something like this?
# of Customers
# of Orders
2
1
1
2
My code so far is:
select count(*) CustomerID
FROM Table1
GROUP BY CustomerID;
Use a double aggregation:
SELECT COUNT(*) AS num_customers, cnt AS num_orders
FROM
(
SELECT CustomerID, COUNT(*) AS cnt
FROM Table1
GROUP BY CustomerID
) t
GROUP BY cnt;
The inner subquery finds the number of orders for each customer. The outer query then aggregates by number of orders and finds out the number of customers having each number of orders.
If you want to sort your tables and your users depending on the number of orders they made, this query should work:
SELECT CustomerID, COUNT(CustomerID) as NbOrder
FROM Table1
GROUP BY(NbOrder)
I believe what you want to do is get the count of orders by customer, first, via aggregation. Then get the count of customers by order count from that query.
SELECT count(*) as count_of_customers, count_of_orders
FROM
(
SELECT customerid, count(*) as count_of_orders
FROM your_table
GROUP BY customerid
) sub
GROUP BY count_of_orders
ORDER BY count_of_orders
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
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).
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
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