Select maximum value of one table depending on 2 other tables - sql

I have 3 tables
Orders (orderID, CustomerID)
Orderlines (orderID, ProdID)
Products (ProdID, CategoryID)!
I want to find the customerID which has the most different "CategoryID" in one order!

To get you there, start with the basic query to get your info:
SELECT o.customer_id
,l.orderid
,COUNT(DISTINCT categoryid) category_cnt
FROM orders o
JOIN orderlines l on l.orderid = o.orderid
JOIN products p ON l.prodid = p.prodid
GROUP BY l.customner_id, l.orderid
order by COUNT(DISTINCT categoryid) desc;
Once you see that this works out, we will add an analytic to this to show you the rank() function
SELECT o.customer_id
,l.orderid
,COUNT(DISTINCT categoryid) category_cnt
, rank() over (order by COUNT(DISTINCT categoryid) desc) as count_rank
FROM orders o
JOIN orderlines l on l.orderid = o.orderid
JOIN products p ON l.prodid = p.prodid
GROUP BY l.customner_id, l.orderid
order by COUNT(DISTINCT categoryid) desc;
Following so far? OK, so now we just need to push this down into a sub-query to get the record(s) ranked #1 (in case more than one customer order matches the top count)
SELECT customer_id, order_id, category_cnt
FROM (
SELECT o.customer_id
,l.orderid
,COUNT(DISTINCT categoryid) category_cnt
, rank() over (order by COUNT(DISTINCT categoryid) desc) as count_rank
FROM orders o
JOIN orderlines l on l.orderid = o.orderid
JOIN products p ON l.prodid = p.prodid
GROUP BY l.customner_id, l.orderid)
WHERE count_rank = 1;

Try;
with data_a as ( --distinct CategoryID cnt
select
o.orderID,
o.customerID,
count(DISTINCT p.CategoryID) cnt
from Orders o
join Orderlines ol.orderID = o.orderID
join Products p on p.ProdID = ol.ProdID
group by o.orderID, o.customerID
),
data as ( --get all count rnk
select
orderID,
customerID,
rank() over (partition by orderID, customerID order by cnt desc) rnk
from data_a
)
select
orderID,
customerID
from data
where rnk = 1

Step by step: Count distinct categories per order first. Then rank your orders, so that the orders with the most categories get rank #1. Then find customers for all orders ranked #1.
select distinct cutomerid
from orders
where orderid in
(
select orderid
from
(
select orderid, rank() over (order by category_count desc) as rnk
from
(
select ol.orderid, count(distinct p.distinctcategroyid) as category_count
from orderlines ol
join products p on p.prodid = ol.prodid
group by ol.orderid
) counted
) ranked
where rnk = 1
);

Something like that i guess
SELECT o.customerID, t.category_cnt
FROM (SELECT l.orderid, COUNT(DISTINCT categoryid) category_cnt
FROM orderlines l
JOIN products p ON l.prodid = p.prodid
GROUP BY l.orderid
ORDER BY category_cnt DESC) t
JOIN orders o ON o.orderid = t.orderid
WHERE rownum < 2

Related

SQL - Find customers who have not ordered in the last year and what they ordered

I am able to find the customers who have not ordered in the last year by doing this:
SELECT O.CustID, MAX(O.ORDERDATE) AS LastOrder FROM Orders O
WHERE LastOrder <= DATEADD(year,-1,GETDATE())
GROUP BY O.CustID
I would like to join a second table (OrderDetails) that contains the order specifics on the field called OrderID.
There is only one item per order. (my data isn't actually customers and orders - but the analogy works for what I am doing).
When I add this OrderID field into the query, my results multiply. This is happening because while when I am grouping, I am grouping by OrderID and CustID, which would pull each specific OrderID.
My question is how would I pull just the last OrderID from the OrderDetails table where the orderID for that customer is over 1 year old.
This is what I have so far
SELECT OD.OrderID, O.CustID, MAX(O.ORDERDATE) AS LastOrder
FROM Orders O
INNER JOIN OrderDetails OD
ON O.OrderID = OD.OrderID
WHERE LastOrder <= DATEADD(year,-1,GETDATE())
GROUP BY OD.OrderID, O.CustID
Using:
WITH cte AS (
SELECT O.*
FROM Orders O
INNER JOIN OrderDetails OD
ON O.OrderID = OD.OrderID
QUALIFY MAX(O.OrderDate) OVER(PARTITION BY O.CustId) <= DATEADD(year,-1,CURRENT_DATE())
)
SELECT *
FROM cte
QUALIFY ROW_NUMBER() OVER(PARTITION BY CustId ORDER BY OrderDate DESC) = 1
So almost completely a cut'n'pase of Lukasz answer BUT, the two QUALIFY filters can be merged:
WITH orders(cust_id, order_date, order_id) AS (
SELECT column1
,to_date(column2,'yyyy-mm-dd')
,column3
FROM VALUES
(1,'2022-01-01',1),
(1,'2021-01-01',2),
(2,'2021-01-01',3),
(3,'2021-01-01',4)
), order_details(order_id, details) AS (
SELECT *
FROM VALUES
(1,'detail 1'),
(2,'detail 2'),
(3,'detail 3'),
(4,'detail 4')
)
SELECT o.cust_id
,o.order_date as last_order_date
,od.details
FROM orders as o
JOIN order_details as od ON o.order_id = od.order_id
QUALIFY max(o.order_date) over(partition by o.cust_id) <= dateadd(year, -1, current_date) AND
ROW_NUMBER() over (partition by o.cust_id ORDER BY o.order_date) = 1
ORDER BY 1;
BUT I would tend to do the filtering first (as ether a sub-select OR CTE) and then join to order_details as the details are not needed, thus you will make it the most obvious to the SQL compiler (which should see the JOIN can be done after)
WITH orders(cust_id, order_date, order_id) AS (
SELECT column1
,to_date(column2,'yyyy-mm-dd')
,column3
FROM VALUES
(1,'2022-01-01',1),
(1,'2021-01-01',2),
(2,'2021-01-01',3),
(3,'2021-01-01',4)
), order_details(order_id, details) AS (
SELECT *
FROM VALUES
(1,'detail 1'),
(2,'detail 2'),
(3,'detail 3'),
(4,'detail 4')
), stale_customers AS (
SELECT *
FROM orders AS o
QUALIFY max(o.order_date) over(partition by o.cust_id) <= dateadd(year, -1, current_date) AND
ROW_NUMBER() over (partition by o.cust_id ORDER BY o.order_date) = 1
)
SELECT o.cust_id
,o.order_date as last_order_date
,od.details
FROM stale_customers as o
JOIN order_details as od ON o.order_id = od.order_id
ORDER BY 1;

How to find the maximum product id in customer wise category in sql?

Here I have the customer id and max order quantity, how can I get the productid column for maximum orderquantity along with the customerid and maxorderqty column.
Database: adventure works
Tables used:salesorerheader,salesorderdetails
SELECT customerid,
Max(totalqty)
FROM (SELECT customerid,
Sum(orderqty) AS Totalqty,
productid AS pdtid
FROM sales.salesorderheader a
INNER JOIN sales.salesorderdetail b
ON a.salesorderid = b.salesorderid
GROUP BY customerid,
productid)A
WHERE customerid = 29825
GROUP BY customerid
If you're interested in finding single record, you can use something like this:
SELECT TOP(1) CustomerID, max(totalqty) AS maxqty, pdtid
FROM
(
SELECT customerid, Sum(orderqty) AS Totalqty, productid AS pdtid
FROM sales.salesorderheader a
INNER JOIN sales.salesorderdetail b
ON a.salesorderid = b.salesorderid
GROUP BY customerid, productid
) A
WHERE CustomerID=29825
GROUP BY CustomerID, pdtid
ORDER BY max(totalqty) DESC
But... if you want to find several records with the same rank, use this:
SELECT *
FROM
(
SELECT RANK() OVER(ORDER BY max(totalqty) DESC) rnk, CustomerID, max(totalqty) AS maxqty, pdtid
FROM
(
SELECT customerid, Sum(orderqty) AS Totalqty, productid AS pdtid
FROM sales.salesorderheader a
INNER JOIN sales.salesorderdetail b
ON a.salesorderid = b.salesorderid
GROUP BY customerid, productid
) A
WHERE CustomerID=29825
GROUP BY CustomerID, pdtid
) B
WHERE rnk = 1
db<>fiddle
Another way is to 'join' sailes details again ;)

SQL Query for counting number of orders per customer and Total Dollar amount

I have two tables
Order with columns:
OrderID,OrderDate,CID,EmployeeID
And OrderItem with columns:
OrderID,ItemID,Quantity,SalePrice
I need to return the CustomerID(CID), number of orders per customer, and each customers total amount for all orders.
So far I have two separate queries. One gives me the count of customer orders....
SELECT CID, Count(Order.OrderID) AS TotalOrders
FROM [Order]
Where CID = CID
GROUP BY CID
Order BY Count(Order.OrderID) DESC;
And the other gives me the total sales. I'm having trouble combining them...
SELECT CID, Sum(OrderItem.Quantity*OrderItem.SalePrice) AS TotalDollarAmount
FROM OrderItem, [Order]
WHERE OrderItem.OrderID = [Order].OrderID
GROUP BY CID
I'm doing this in Access 2010.
You would use COUNT(DISTINCT ...) in other SQL engines:
SELECT CID,
Count(DISTINCT O.OrderID) AS TotalOrders,
Sum(OI.Quantity*OI.SalePrice) AS TotalDollarAmount
FROM [Order] O
INNER JOIN [OrderItem] OI
ON O.OrderID = OI.OrderID
GROUP BY CID
Order BY Count(DISTINCT O.OrderID) DESC
Which Access unfortunately does not support. Instead you can first get the Order dollar amounts and then join them before figuring the order counts:
SELECT CID,
COUNT(Orders.OrderID) AS TotalOrders,
SUM(OrderAmounts.DollarAmount) AS TotalDollarAmount
FROM [Orders]
INNER JOIN (SELECT OrderID, Sum(Quantity*SalePrice) AS DollarAmount
FROM OrderItems GROUP BY OrderID) AS OrderAmounts
ON Orders.OrderID = OrderAmounts.OrderID
GROUP BY CID
ORDER BY Count(Orders.OrderID) DESC
If you need to include Customers that have orders with no items (unusual but possible), change INNER JOIN to LEFT OUTER JOIN.
Create a query which uses your 2 existing queries as subqueriers, and join the 2 subqueries on CID. Define your ORDER BY in the parent query instead of in a subquery.
SELECT
sub1.CID,
sub1.TotalOrders,
sub2.TotalDollarAmount
FROM
(
SELECT
CID,
Count(Order.OrderID) AS TotalOrders
FROM [Order]
GROUP BY CID
) AS sub1
INNER JOIN
(
SELECT
CID,
Sum(OrderItem.Quantity*OrderItem.SalePrice)
AS TotalDollarAmount
FROM OrderItem INNER JOIN [Order]
ON OrderItem.OrderID = [Order].OrderID
GROUP BY CID
) AS sub2
ON sub1.CID = sub2.CID
ORDER BY sub1.TotalOrders DESC;

Cannot reference subquery column by outer order by

The ORDER BY clause below is causing an error. How can I reference the TotalPrice column in ORDER BY clause:
SELECT * FROM
(
SELECT O.OrderID,
(SELECT SUM(SubTotal) FROM DB_OrderDetails OD WHERE OD.OrderID = O.OrderID) AS TotalPrice,
ROW_NUMBER() OVER (ORDER BY TotalPrice) AS RowNum
FROM DB_Orders O
) Orders
You cannot reference it by its name in the same sub-query, you nave to do it in the outer query:
SELECT orders.*,
ROW_NUMBER() OVER (ORDER BY TotalPrice) AS RowNum FROM
(
SELECT O.OrderID,
(SELECT SUM(SubTotal) FROM DB_OrderDetails OD
WHERE OD.OrderID = O.OrderID) AS TotalPrice
FROM DB_Orders O
) Orders
How about this:
select
o.orderID,
sum(od.SubTotal) as TotalPrice,
row_number() over (order by sum(od.SubTotal)) as RowNum
from DB_Orders o
join DB_OrderDetails od
on o.OrderID = od.OrderID
group by o.OrderID
Here is the example: SQL Fiddle

SELECT TOP record for each year

I am trying to recap on my sql skill, now I am trying to run a simple query on northwinddb to show me the top customer for each year, but as soon as I use the TOP function only 1 record gets display no matter on what I partition by, This is my T-SQL code
SELECT DISTINCT TOP 1 C.CompanyName
, YEAR(O.OrderDate) AS Year
, SUM(Quantity) OVER(PARTITION BY C.CompanyName, YEAR(O.OrderDate)) AS Total
FROM Customers C JOIN Orders O
ON C.CustomerID = O.CustomerID JOIN [Order Details] OD
ON O.OrderID = OD.OrderID
You can do this bit more compactly in SQL Server 2008 as follows:
select top (1) with ties
C.CompanyName,
Year(O.OrderDate) as Yr,
sum(OD.Quantity) as Total
from Orders as O
join Customers as C on C.CustomerID = O.CustomerID
join "Order Details" as OD on OD.OrderID = O.OrderID
group by C.CompanyName, Year(O.OrderDate)
order by
row_number() over (
partition by Year(O.OrderDate)
order by sum(OD.Quantity) desc
);
Thank you for the help. I found a way that allows me to change the number of top customers i want to see for each year. By using Sub queries and Row_Number
SELECT CompanyName
,yr
,Total
FROM(SELECT CompanyName
, yr
, Total
, ROW_NUMBER() OVER(PARTITION BY yr ORDER BY yr, Total DESC) AS RowNumber
FROM(SELECT DISTINCT CompanyName
, YEAR(O.OrderDate) AS yr
, SUM(OD.Quantity) OVER(PARTITION BY CompanyName
, YEAR(O.OrderDate)) As Total
FROM Customers C JOIN Orders O
ON C.CustomerID = O.CustomerID JOIN [Order Details] OD
ON O.OrderID = OD.OrderID) Tr)Tr2
Where RowNumber <= 1
Three steps: first sum quantities grouped by company and year, then order the results by quantity, then filter first row by group only.
; WITH sums as (
SELECT C.Companyname, YEAR(O.OrderDate) AS Year, sum (Quantity) Total
FROM Customers C JOIN Orders O
ON C.CustomerID = O.CustomerID JOIN [Order Details] OD
ON O.OrderID = OD.OrderID
group by C.Companyname, YEAR(O.OrderDate)
)
with ordering as (
select Companyname, Year, Total,
row_number() over (partition by CompanyName, Year order by Total desc) rownum
from sums
)
select *
from ordering
where rownum = 1