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
Related
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
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
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;
prepare the monthly sales report for the customer who has the maximum sales, for the below table
This is an example of how you can retrieve in 'one' query:
the id of the customer who has the maximum sales
the sales summary of this customer
First the query that brings the customer_id and the corresponding summary of sales is like:
select customer_id, sum(sales) as sumsales from mytable group by customer_id;
To this query, a rank has to be added to the descending order of Sales summaries. This will allow to select a single record according to its rank later. So, a wrap is necessary:
select customer_id, sumsales, rank() over (order by sumsales desc) as rnk from
(select customer_id, sum(sales) as sumsales from mytable group by customer_id);
Now that the ranked entries are available, the first ranked record has to be selected:
select customer_id, sumsales from
(select customer_id, sumsales, rank() over (order by sumsales desc) as rnk from
(select customer_id, sum(sales) as sumsales from mytable group by customer_id)
)
where rnk=1;
However, this may not be the most effective way to achieve this.
EDIT:
In order to avoid a wrapping layer, just to add the rank, it is possible to add the rank foeld to the first internal query:
select customer_id, sum(sales) as sumsales, rank() over (order by sum(sales) desc) as rnk
from mytable group by customer_id;
And then, a single wrapping query is needed to select the first ranked record as:
select customer_id, sumsales from (
select customer_id, sum(sales) as sumsales, rank() over (order by sum(sales) desc) as rnk
from mytable group by customer_id
)
where rnk=1;
Reference to other similar relevant answers here.
I have a subquery that joins my customers and transactions table, aliased as jq. I want to create a ranking of each customer's purchases (transactions) by order timestamp (order_ts). So I did,
SELECT customer_id,
order_id,
order_ts,
RANK() OVER (PARTITION BY customer_id ORDER BY order_ts ASC) AS purchase_rank,
amount
FROM jq GROUP BY customer_id
ORDER BY customer_id;
Alongside with the purchase_rank column, I also want to know how many total purchases the customer have done. So this becomes:
SELECT customer_id,
order_id,
order_ts,
RANK() OVER (PARTITION BY customer_id ORDER BY order_ts ASC) AS purchase_rank,
// total purchases of this customer, counted by order_id
amount
FROM jq GROUP BY customer_id
ORDER BY customer_id;
Some order_ids are duplicated, so I want to count distinctly. How do I do this in MS SQL Server and Postgres without joining to a subquery?
Microsoft SQL Server
SELECT customer_id,
order_id,
order_ts,
purchase_rank,
MAX(cnt) OVER(PARTITION BY customer_id) AS purchase_cnt,
amount
FROM
(
SELECT customer_id,
order_id,
order_ts,
RANK() OVER (PARTITION BY customer_id ORDER BY order_ts ASC) AS purchase_rank,
DENSE_RANK() OVER (PARTITION BY customer_id ORDER BY order_id ASC) AS cnt,
amount
FROM jq
-- GROUP BY customer_id
) AS D
ORDER BY customer_id;
Sorry, I am not familiar with postgresql.