Using RANK with COUNT(DISTINCT) aggregation at the same time - sql

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.

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

Aggregation level is off (Postgresql)

I have Order data for 2 customers and their order. And I am trying to calculate what the sum for the price is for every customter for that specific order only for product N
Table:
This is my query:
select Customer_ID, Order_ID, Sales_Date,
sum(Price) over (partition by Customer_ID, Order_ID order by Customer_ID, Order_ID)
from orders
group by 1,2,3, Price
order by;
For some reason I do not understand it gives me several rows per same customer. I am trying to get only one row generated per customer and order for product N
This is my current Output:
Desired Outcome:
Why are you using window functions? I think you just want aggregation:
select Customer_ID, Order_ID, Sales_Date,
sum(Price)
from orders
group by 1,2,3;
If you only want one product, add where product = 'N'.

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;

prepare the monthly sales report for the customer who has the maximum sales, for the below table

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.