Get Last Order ID and Date Per Customer in Oracle - sql

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;

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

How do I show the previous 2 orders a customer made?

I am learning SQL server and I am stuck on a question.
I need to write a query that shows each customers last order that he placed and the order before the last one he made.
Thank you for your help!
Edit: So far I have this:
SELECT SalesOrderID, CustomerID, per.FirstName, per.LastName, OrderDate as "Latest Order Date"
FROM (
SELECT *,
Row_Number() OVER (PARTITION BY CustomerID ORDER BY OrderDate desc) as 'Rank'
FROM sales.SalesOrderHeader head
) a join Person.Person per
on CustomerID = per.BusinessEntityID
WHERE Rank = 1
As you can see, I am pretty close. I just need to add a column that shows the order before the latest order date.
Sorry, I'm new to the site (long time viewer, first time poster)
ty!
You need to combine both ROW_NUMBER and LEAD
LEAD is better in this case, because LAG needs the rows sorted in the opposite direction from the ROW_NUMBER
SELECT head.SalesOrderID, CustomerID, per.FirstName, per.LastName, OrderDate as LastOrder, head.PreviousOrder
FROM (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY CustomerID ORDER BY OrderDate DESC) as rnk,
LEAD(OrderDate) OVER (PARTITION BY CustomerID ORDER BY OrderDate DESC) as PreviousOrder
FROM sales.SalesOrderHeader head
) head
JOIN Person.Person per ON head.CustomerID = per.BusinessEntityID
WHERE head.rnk = 1;

select the latest balance for each product based on store (SQL)

I'm trying to get latest balance for each product based on storeid . But I don know why get result duplicate stock name sasi.
Sql Query
select stockname, balance,updatedat,storename,s1.productid,s1.storeid
from stockmovement s1
inner join (select storeid, productid, max(updatedat) as maxdate
from stockmovement
group by storeid,productid) s2
on s2.storeid = s1.storeid and s2.maxdate = s1.updatedat
This is a greatest-n-per-group problem, and they can be solved efficiently using distinct on () in Postgres:
select distinct on (storeid, productid) *
from stockmovement
order by storeid, productid, updatedat desc
This is typically faster than the equivalent solution using window functions.
Try below query using row_number():
select stockname, balance, bupdatedat, storename,productid,storeid from
(
select stockname, balance, bupdatedat, storename,productid,storeid, row_number() over
(partition by storeid,productid order by bupdatedat date desc) rn
)t where rn=1
select stockname, balance, updatedat, storename,productid,storeid from
(
select stockname, balance, updatedat, storename,productid,storeid, row_number() over
(partition by storeid,productid order by updatedat desc )rn from stockmovement
)t where rn=1

SQL show the first and last value in count

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

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