Mysql query for getting monthwise n th highest spending customer - mysql

I am using sakila.payment table.
Columns: payment_id, customer_id, staff_id, rental_id, amount, payment_date, update_date
I am using this query to get customers spending the highest amount for each month.
How can I get the Nth highest spending customer for each month?
select customer_id,`month`,max(total_amount) from
(SELECT customer_id,count(customer_id) as `count`,month(payment_date) as `month`,sum(amount) as total_amount
FROM sakila.payment
group by month(payment_date),customer_id
order by `month` asc, `total_amount` desc)t
group by `month`

Try the following, if you are using MySQL 8.0 then it will work with row_number()
select
customer_id,
month,
total_amount
from
(
select
customer_id,
month,
total_amount,
row_number() over (partition by month order by total_amount desc) as rnk
from
(
select
customer_id,
count(customer_id) as `count`,
month(payment_date) as `month`,
sum(amount) as total_amount,
from sakila.payment
group by
month(payment_date),
customer_id
) cal
) mnt
where rnk = 1

Related

how to set order by in between two union querys

SELECT month,sum(pcs) as pcs,sum(carat) as carat,sum(amount) as amount from (
SELECT
DATE_FORMAT(StockInwardDate, '%b-%Y') AS Month,
COUNT(*) as pcs,
ROUND(SUM(Carat),2) as carat,
ROUND(SUM(TotalAmount),2) as amount
FROM tbl_sales
GROUP BY DATE_FORMAT(StockInwardDate, '%m-%Y')
UNION
SELECT
DATE_FORMAT(StockInwardDate, '%b-%Y') AS Month,
COUNT(*) as pcs,
ROUND(SUM(Carat),2) as carat,
ROUND(SUM(TotalAmount),2) as amount
FROM tbl_stock
GROUP BY DATE_FORMAT(StockInwardDate, '%m-%Y')
) as tbl
GROUP BY tbl.Month
ORDER by tbl.Month DESC
This above is my query but tbl.Month in order by which is equals to DATE_FORMAT(StockInwardDate, '%m-%Y') But instead of DATE_FORMAT(StockInwardDate, '%m-%Y') I want to get order by StockInwardDate only this
Is there any way to get This in order by StockInwardDate

How do I find the 2nd most recent order of a customer

I have the customers first order date and last order date by doing MIN and MAX on the created_at field (grouping by email), but I also need to get the customers 2nd most recent order date (the order date right before the last orderdate )
SELECT
customer_email,
COUNT(entity_id) AS NumberOfOrders,
MIN(CONVERT_TZ(created_at,'UTC','US/Mountain')) AS 'FirstOrder',
MAX(CONVERT_TZ(created_at,'UTC','US/Mountain')) AS 'MostRecentOrder',
SUM(grand_total) AS TotalRevenue,
SUM(discount_amount) AS TotalDiscount
FROM sales_flat_order
WHERE
customer_email IS NOT NULL
AND store_id = 1
GROUP BY customer_email
LIMIT 500000
Use window function ROW_NUMBER() (available in MySQL 8.0):
SELECT *
FROM (
SELECT
t.*,
ROW_NUMBER() OVER(PARTITION BY customer_email ORDER BY created_at) rn_asc,
ROW_NUMBER() OVER(PARTITION BY customer_email ORDER BY created_at DESC) rn_desc
FROM sales_flat_order
WHERE customer_email IS NOT NULL AND store_id = 1
) x
WHERE rn_asc = 1 OR rn_desc <= 2
This will get you the earlierst and the two latest orders placed by each customer.
Note: it is unclear what the timezone conversions are intended for. I left them apart, since they obviously do not affect the sorting order; feel free to add them as per your use case.
If you want a single record per customer, along with its total count of orders, and the date of his first, last, and last but one order, then you can aggregate in the outer query:
SELECT
customer_email,
NumberOfOrders,
MAX(CASE WHEN rn_asc = 1 THEN created_at END) FirstOrder,
MAX(CASE WHEN rn_desc = 1 THEN created_at END) MostRecentOrder,
MAX(CASE WHEN rn_desc = 2 THEN created_at END) MostRecentButOneOrder,
TotalRevenue,
TotalDiscount
FROM (
SELECT
customer_email,
created_at,
COUNT(*) OVER(PARTITION BY customer_email) NumberOfOrders,
SUM(grand_total) OVER(PARTITION BY customer_email) TotalRevenue,
SUM(discount_amount) OVER(PARTITION BY customer_email) TotalDiscount,
ROW_NUMBER() OVER(PARTITION BY customer_email ORDER BY created_at) rn_asc,
ROW_NUMBER() OVER(PARTITION BY customer_email ORDER BY created_at DESC) rn_desc
FROM sales_flat_order
WHERE customer_email IS NOT NULL AND store_id = 1
) x
GROUP BY customer_email

how to retrieve the difference between the two given dates in SQL

How to find the Longest Booking ID for the given two dates and Costliest Booking ID for the given cost.
Here we have the 13 days difference so we are getting the longest booking id as 1.
what are the approach to archive this using sql query.
this will work:
(SELECT 'Total Booking Count' AS Label, COUNT(*) AS Value FROM bookings)
UNION ALL
(SELECT 'Longest Booking Id', booking_id FROM bookings ORDER BY DATEDIFF(enddate, startdate) DESC LIMIT 1)
UNION ALL
(SELECT 'Costliest Booking Id', booking_id FROM bookings ORDER BY (tariff*DATEDIFF(enddate, startdate)) DESC LIMIT 1)
with data
as (select *
,row_number() over(order by datediff(dd,end_date,start_date) desc) as rnk_time
,row_number() over(order by tarrif desc) as rnk_cost
,count(*) over(partition by 1) as tot_cnt
from your_table
)
select 'Total Booking count',tot_cnt
from data
where rnk_time=1
union all
select 'Longest Booking id',booking_id
from data
where rnk_time=1
union all
select 'Costliest Booking id',booking_id
from data
where rnk_cost=1

MySQL Select Top Row Grouping By Another Row

I have a sales table and I want to get each members most frequently shopped store in the last 3 months. The following query will get the every member with every store, but I want just one store per member.
SELECT member_id, store_id, COUNT(DISTINCT docket) as docket_count, SUM(dollar_amount) as dollars
FROM sales
WHERE TIMESTAMPDIFF(MONTH, sale_date, CURDATE()) < 3
GROUP BY member_id, store_id
ORDER BY member_id, docket_count DESC, dollars DESC
Or to get the top store for a single member
SELECT store_id, COUNT(DISTINCT docket) as docket_count, SUM(dollar_amount) as dollars
FROM sales
WHERE TIMESTAMPDIFF(MONTH, sale_date, CURDATE()) < 3
AND member_id = 1
GROUP BY store_id
ORDER BY docket_count DESC, dollars DESC
This is tricky. In MySQL, this can be easiest using the group_concat()/substring_index() trick:
SELECT member_id,
SUBSTRING_INDEX(GROUP_CONCAT(store_id ORDER BY docket_count DESC dollars DESC), ',', 1) as Most_Common_Store
FROM (SELECT member_id, store_id, COUNT(DISTINCT docket) as docket_count,
SUM(dollar_amount) as dollars
FROM sales
WHERE sale_date >= CURDATE() - interval 3 month
GROUP BY member_id, store_id
) ms
GROUP BY member_id;

How can i get count of customers per day by unique and repeat customer for specific date?

I am trying to get a result from my order table to get list of counts of customers who 1st time ordered and repeat orders. Something like below.
Date 1st time time repeat order
2014-09-01 43 90
2014-09-02 3 45
2014-09-03 12 30
2014-09-04 32 0
2014-09-05 1 98
I am beginner in sql and i ma using mysql.
My table structure is like.
OrderNumber int
OrderDate datetime
CustomerID int
I have tried this query in mysql but it only gives me first timed ordered count.
SELECT DATE(OrderDate), COUNT(*)
FROM orders T JOIN (
SELECT MIN(OrderDate) as minDate, CustomerID
FROM orders
GROUP BY CustomerID) T2 ON T.OrderDate = T2.minDate AnD T.CustomerID = T2.CustomerID
GROUP BY DATE(T.OrderDate)
You can get the total orders per day by grouping on OrderDate:
SELECT OrderDate, COUNT(OrderNumber) AS total FROM orders GROUP BY OrderDate
And you can get the no. of first orders per day from the following query :
SELECT OrderDate, COUNT(q1.CustomerID) AS first FROM (SELECT CustomerID, min(OrderDate) AS OrderDate FROM orders GROUP BY CustomerID)q1 GROUP BY q1.OrderDate
Now join these two on OrderDate to get the distribution of first and repeated orders :
SELECT a.OrderDate, a.first, (b.total - a.first) AS repeated FROM
(SELECT OrderDate, COUNT(q1.CustomerID) AS first FROM (SELECT CustomerID, min(OrderDate) AS OrderDate FROM orders GROUP BY CustomerID)q1 GROUP BY q1.OrderDate)a
JOIN
(SELECT OrderDate, COUNT(OrderNumber) AS total FROM orders GROUP BY OrderDate)b
on(a.OrderDate = b.OrderDate)
A slightly complicated query but this should do:
First Time Users: Just Group by customerID to get the min orderdate and then group by on that date to get the number of new users on a particular day. Query would look like this:
select date(mdate) as day, COUNT(*) from (select customerid, min(orderdate) as mDate from orders GROUP BY CustomerID)q1 GROUP BY day;
Repeat Users: First filter out all such orderno which were placed as first orders and then do a group by orderdate to get repeat. Query would be :
select date(orderdate) day, COUNT(*) from (select * from orders where orderno not in (select orders.orderno from orders JOIN (select customerid, min(orderdate) as mdate from orders GROUP BY CustomerID)as order2 ON (orders.customerid = order2.customerid) and (orders.orderdate = order2.mdate))) as q1 GROUP BY day;
You can do a join on day for both these queries to get combined results in a way you mentioned. Let me know if doesn't work
EDIT:
This would be the complete query: Here I am doing a UNION on both left and right outer joins since it might happen that you come across where there are no new requests or no repeated requests. This would take care of both the scenarios.
select q2.*, q3.repeated from (select date(mdate) as day, COUNT(*) as first from (select customerid, min(orderdate) as mDate from orders GROUP BY CustomerID)q1 GROUP BY day) as q2 LEFT OUTER JOIN (select date(orderdate) day, COUNT(*) as repeated from (select * from orders where orderno not in (select orders.orderno from orders JOIN (select customerid, min(orderdate) as mdate from orders GROUP BY CustomerID)as order2 ON (orders.customerid = order2.customerid) and (orders.orderdate = order2.mdate))) as q1 GROUP BY day) as q3 on q2.day = q3.day UNION select q2.*, q3.repeated from (select date(mdate) as day, COUNT(*) as first from (select customerid, min(orderdate) as mDate from orders GROUP BY CustomerID)q1 GROUP BY day) as q2 RIGHT OUTER JOIN (select date(orderdate) day, COUNT(*) as repeated from (select * from orders where orderno not in (select orders.orderno from orders JOIN (select customerid, min(orderdate) as mdate from orders GROUP BY CustomerID)as order2 ON (orders.customerid = order2.customerid) and (orders.orderdate = order2.mdate))) as q1 GROUP BY day) as q3 on q2.day = q3.day
this is my answer but not sure is still can improve.
SELECT userID, COUNT(*) AS repeat_order_cnt FROM
(SELECT DATE(OrderDate) AS order_DT, userID, COUNT(*) AS no_of_order FROM order
AND YEAR(orderDate) = '2015'
AND MONTH(orderDate) = '01'
GROUP BY order_DT,userID) AS order2
GROUP BY userID
HAVING COUNT(*) > 1