SQL show the first and last value in count - sql

I need to write a query that show the max and the min count of order the customer order.
I tried:
(SELECT TOP 1 CustomerID, COUNT(*) AS Number_Of_Orders
FROM Orders
GROUP BY CustomerID
ORDER BY COUNT(*) ASC)
UNION ALL
(SELECT TOP 1 CustomerID, COUNT(*) AS Number_Of_Orders
FROM Orders
GROUP BY CustomerID
ORDER BY COUNT(*) DESC)
But I don't succeed to union between the output, I got the error Incorrect syntax near the keyword 'ORDER'.
How can I get that?

I'm not sure I would want to run the aggregation twice, so use window functions:
SELECT CustomerID, Number_Of_Orders
FROM (SELECT CustomerID, COUNT(*) AS Number_Of_Orders,
ROW_NUMBER() OVER (ORDER BY COUNT(*) ASC) as seqnum_asc,
ROW_NUMBER() OVER (ORDER BY COUNT(*) DESC) as seqnum_desc
FROM Orders
GROUP BY CustomerID
) c
WHERE seqnum_asc = 1 OR seqnum_desc = 1;

You can't use ORDER BY inside the queries that you want to unify with UNION, but you can do this:
SELECT * FROM
(SELECT TOP 1 CustomerID, COUNT(*) AS Number_Of_Orders
FROM Orders
GROUP BY CustomerID
ORDER BY COUNT(*) ASC) t
UNION ALL
SELECT * FROM
(SELECT TOP 1 CustomerID, COUNT(*) AS Number_Of_Orders
FROM Orders
GROUP BY CustomerID
ORDER BY COUNT(*) DESC) t
This does the trick although it is inefficient because you execute twice the same code and sort twice.

You can use window functions for this. This will give you multiple customers for min/max if there are ties (fiddle):
SELECT CustomerID
, OrderCount
, CASE WHEN OrderCount = MinOrderCount THEN 'Customer with min orders'
WHEN OrderCount = MaxOrderCount THEN 'Customer with max orders' END AS Type
FROM (
SELECT CustomerID
, COUNT(*) AS OrderCount
, MIN(COUNT(*)) OVER () AS MinOrderCount
, MAX(COUNT(*)) OVER () AS MaxOrderCount
FROM Orders
GROUP BY CustomerID
) AS x
WHERE OrderCount = MinOrderCount OR OrderCount = MaxOrderCount

Related

ROW_NUMBER() syntax

cte3 as
(select
customer_id,
count(order_id) as total_orders,
ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY order_id) AS order_num
from orders
group by customer_id)
I'm new to SQL on Oracle. I have a problem with the ROW_NUMBER() analytical function and its syntax. What I want is to show every order from every customer like so:
order_id cust_id total_orders order_num
128128 1 2 1
256256 1 2 2
512512 2 1 1
When I try to run the query, it gives me:
ORA-00979: not a GROUP BY expression
I did a little research on the error and tried this:
group by customer_id, order_id --> gives me correct row_num column, but duplicate results and wrong
group by customer_id/order_id --> doesn't work, as it raises ORA-00979
This will work.
For more help we need more data from you.
select customer_id,
count(order_id) as total_orders,
ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY order_id)
from orders
group by customer_id, order_id
Every column that is not a part of aggregate function needs to be in a group by expression
I can just suggest one of the solutions(for what I believe you need):
First option is to do a join with a subquery.
select o1.order_id,
o1.customer_id,
o3.total_orders,
ROW_NUMBER() OVER (PARTITION BY o1.customer_id ORDER BY o1.order_id)
from orders o1
left join (select count(o2.order_id) as total_orders
, o2.customer_id as ci
from orders o2
group by customer_id) o3 on o1.customer_id = o3.ci
OR:
Second option is to use count analytic function:
select order_id,
customer_id,
count(order_id) over (partition by customer_id) as total_orders,
ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY order_id)
from orders
Here is the demo

SQL: How to select the highest priced used item for each day

I need to produce a query that would give me the highest priced used product for each day where the total price of products sold that day exceeds 200.
SELECT *, max(price)
FROM products
WHERE products.`condition` = 'used' and products.price > 200
GROUP BY date_sold
Here is my products table http://prntscr.com/of3hjd
You could try using a join with sum for price > 200 group by date_sol
select m.date_sold, max(m.price)
from my_table m
inner join (
select date_sold, sum(price)
from my_table
group by date_sold
having sum(price)>200
) t on t.date_sold = m.date_sold
group by m.date_sold
You can use window functions for this:
select p.*
from (select p.*,
sum(price) over (partition by date_sold) as sum_price,
row_number() over (partition by date_sold, condition order by price desc) as seqnum
from products p
) p
where sum_price > 200 and
condition = 'used' and
seqnum = 1;
SELECT *, max(price) FROM products
where products.`condition` = 'used' and sum(products.price) > 200
GROUP BY day(date_sold)

Selecting City from Customer ID in SQL

Customer have ordered from different cities. Thus we have multiple cities against same customer_id. I want to display that city against customer id which has occurred maximum number of times , in case where customer has ordered same number of orders from multiple cities that city should be selected from where he has placed last order. I have tried something like
SELECT customer_id,delivery_city,COUNT(DISTINCT delivery_city)
FROM analytics.f_order
GROUP BY customer_id,delivery_city
HAVING COUNT(DISTINCT delivery_city) > 1
WITH cte as (
SELECT customer_id,
delivery_city,
COUNT(delivery_city) as city_count,
MAX(order_date) as last_order
FROM analytics.f_order
GROUP BY customer_id, delivery_city
), ranking as (
SELECT *, row_number() over (partition by customer_id
order by city_count DESC, last_order DESC) as rn
FROM cte
)
SELECT *
FROM ranking
WHERE rn = 1
select customer_id,
delivery_city,
amount
from
(
select t.*,
rank() over (partition by customer_id order by amount asc) as rank
from(
SELECT customer_id,
delivery_city,
COUNT(DISTINCT delivery_city) as amount
FROM analytics.f_order
GROUP BY customer_id,delivery_city
) t
)
where rank = 1

Get Last Order ID and Date Per Customer in Oracle

It's an orcale database and I'm trying to get the last order ID and its date for each customer. I'm familiar enough with MySQL to be able to write something like the following in a MySQL system:
https://www.w3schools.com/SQL/trysql.asp?filename=trysql_select_all
SELECT CustomerID, COUNT(OrderID), MAX(OrderDate) as Last_Order_Date,
(SELECT OrderID FROM Orders O2
WHERE O2.CustomerId = O1.CustomerId
ORDER BY OrderDate DESC LIMIT 1)
AS LAST_ORDER_ID
FROM [Orders] O1
GROUP BY CustomerID
ORDER BY CustomerID
However I haven't been able to write the equivalent for an Oracle database. The part that trips me up is I have to write a subquery for the order by, then pick the top 1... but that also need to go into my bigger query of getting the last order for each customer.
Use MAX( ... ) KEEP ( DENSE_RANK LAST ... ):
SELECT CustomerID,
COUNT( OrderID ),
MAX( OrderDate ) AS Last_Order_Date,
MAX( OrderID ) KEEP ( DENSE_RANK LAST ORDER BY OrderDate ) AS Last_Order_ID
FROM Orders
GROUP BY CustomerID
ORDER BY CustomerID;
or use analytic functions:
SELECT CustomerID,
Num_Orders,
OrderDate AS Last_Order_Date,
OrderID AS Last_Order_ID
FROM (
SELECT o.*,
COUNT( OrderID ) OVER ( PARTITION BY CustomerID ) AS Num_Orders,
ROW_NUMBER() OVER ( PARTITION BY CustomerID
ORDER BY OrderDate DESC, OrderID DESC ) AS rn
FROM orders o
)
WHERE rn = 1
ORDER BY CustomerID;

Grab top 5 rows and combine the rest SQL Server

I have a column for group name and a column for amount spent.
I need to sum the amounts group them based on the group name and then grab the highest five. After that, I need to combine the the rest into it's own group w/ a total of their amount spent. This is what i have right now
SELECT groupName, SUM(amount) AS theAmountSpent
FROM purchases
GROUP BY groupName
ORDER BY theAmountSpent DESC
This groups and orders them, but i dont know how to then grab the remaining groups to combine them. Any help would be appreciated.
Alternate CTE-approach using row_number() (SQL Server 2005+):
WITH cte AS (
SELECT ROW_NUMBER() OVER (ORDER BY (SUM(amount)) DESC) AS num,
groupName, SUM(amount) AS theAmountSpent
FROM purchases
GROUP BY groupName
)
SELECT groupName, theAmountSpent FROM cte WHERE num BETWEEN 1 AND 5 --top 5
UNION ALL
SELECT 'Sum rest', SUM(theAmountSpent) FROM cte WHERE num > 5 -- sum of rest
If I'm understanding you correctly, this should do it:
SELECT top 5 groupName, SUM(amount) AS theAmountSpent
into #tempSpent FROM purchases
GROUP BY groupName
ORDER BY theAmountSpent DESC
Select * from #tempSpent -- get the top 5
--get sum for the rest
SELECT SUM(amount) AS theAmountSpent
FROM purchases
where groupName not in (select groupName from #tempSpent)
Drop table #tempSpent
Another idea from Larsts code:
WITH cte
AS
(
SELECT case
when ROW_NUMBER() OVER (ORDER BY (SUM(amount)) DESC) <=5
then ROW_NUMBER() OVER (ORDER BY (SUM(amount)) DESC)
else 6 end AS num
, groupName
, SUM(amount) AS theAmountSpent
FROM purchases
GROUP BY groupName
)
SELECT num
, max(groupName)
, sum(theAmountSpent )
FROM cte
group by num