MYSQL inline view query (top customer) - mysql

I try to make a query, so that I can see who is the top customer in a month (every month since begin till now).
Now I have the tables:
orders (orderID, orderdate, customerID, Netamount, tax, totalamount)
orderline (orderlineID, orderID, prodID, quantity, orderdate)
customer (firstname lastname zip creditcardtype etc.)
I think the other tables aren't necessarily here.
Of course there are customers who never bought a thing and customers who already bought plenty of times.
Now I used this query:
SELECT customerid, Sum(netamount)
FROM orders
GROUP BY customerid limit 1000000;
Now I see all customers who already bought sth. with the total amount they paid.
With the query
SELECT YEAR ( Orderdate ) Year ,
MONTHNAME ( Orderdate ) Month ,
COUNT(*) TotOrd ,
FROM orders
GROUP BY YEAR ( Orderdate ),
MONTH ( Orderdate );
I get a table where each row shows me the Year Month Total order (placed in that month).
Still I want just to see the Top Customer of a month.
I searched a lot in the internet still couldn't find that what I want (maybe I just googled wrong). I know that I need at least one inline view still no idea how to realize it.
Hope someone can help me out here.

You need to join back to the data to get the top customer. So, first calculate the maximum amount in each month, then join back to get the customer with that amount:
select my.year, my.month, myc.customerid, myc.totord
from (select year, month, max(totord) as maxtotord
from (SELECT YEAR ( Orderdate ) Year, MONTHNAME ( Orderdate ) Month, customerid, COUNT(*) TotOrd ,
FROM orders
GROUP BY YEAR ( Orderdate ), MONTH ( Orderdate ), customerid
) myc
group by year, month
) my join
(SELECT YEAR ( Orderdate ) Year, MONTHNAME ( Orderdate ) Month, customerid, COUNT(*) TotOrd ,
FROM orders
GROUP BY YEAR ( Orderdate ), MONTH ( Orderdate ), customerid, count(*) as totord
) myc
on my.year = myc.year and my.month = myc.month and my.maxtotord = myc.totord
Note that this is untested, so there might be a syntax error.
Also, this returns multiple customers if there are multiple customers with the max value.
Finally, this is much easier in almost any other database, because most databases now support the row_number() function.

It's a group-wise max problem but unfortunately MySQL doesn't support window functions or CTE so this can be messy.
SELECT s1.year,s1.month,s1.customerid,s1.totord FROM
(SELECT YEAR ( Orderdate ) Year ,
MONTHNAME ( Orderdate ) Month ,
customerid,
COUNT(*) TotOrd
FROM orders
GROUP BY YEAR ( Orderdate ),
MONTH ( Orderdate ),customerid) as s1
LEFT JOIN
(SELECT YEAR ( Orderdate ) Year ,
MONTHNAME ( Orderdate ) Month ,
customerid,
COUNT(*) TotOrd
FROM orders
GROUP BY YEAR ( Orderdate ),
MONTH ( Orderdate ),customerid) as s2
ON
s1.year=s2.year AND s1.month=s2.month AND s2.TorOrd>s1.TotOrd AND s1.customerid>s2.customerid
WHERE s2.customerid IS NULL;
In case of doubles it will return the customer with lower id.

Related

Mysql query for getting monthwise n th highest spending customer

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

Get the most popular product per day, MySQL for Magento

I need to get a table that contains the most popular product sold per day. All data is stored in Magento, and I use MySQL to write the query. The only table I need is Sales_flat_order_item table.
The final table should have 3 columns: Date, Product SKU, and number of units sold of the most popular product that day - MaxQty.
I came up with the query that works for me, but I would like to know how it can be improved since I use the same subquery twice in my code:
1 Select Date, Product Id, Sku, and Quantity from sales_flat_order_item - Subquery1
2 Select Date and Maximum Quantity from Subquery1 - Subquery2
3 Join them together knowing that dates should be the same, and Quantity from Subquery1 should be equal to Maximum Quantity from Subquery2
SELECT DATE( sq2.created_at ) AS CreatedAt, sq0.sku AS SKU, sq2.MaxQty
FROM (
SELECT created_at, product_id, sku, SUM( qty_ordered ) AS qty
FROM `sales_flat_order_item`
GROUP BY DATE( created_at ) , product_id
) AS sq0
JOIN (
SELECT sq.created_at, MAX( sq.qty ) AS MaxQty
FROM (
SELECT created_at, product_id, SUM( qty_ordered ) AS qty
FROM `sales_flat_order_item`
GROUP BY DATE( created_at ) , product_id
) AS sq
GROUP BY DATE( sq.created_at )
) AS sq2 ON DATE( sq2.created_at ) = DATE( sq0.created_at )
AND sq2.MaxQty = sq0.qty
GROUP BY DATE( CreatedAt )
I believe this should do what you want.
I added a WHERE clause to run it only for this month, in case you have a huge database so it should not take much time.
SELECT day, sku, MAX(qty_total) AS qty FROM (
SELECT DATE(created_at) AS day, sku, SUM(qty_ordered) AS qty_total
FROM `sales_flat_order_item`
WHERE created_at > '2015-07%'
GROUP BY sku, day
ORDER BY qty_total DESC
) AS item_count
GROUP BY day

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

Name of customer with highest sale monthwise

I have a sales table from which I select the total sales per month , highest sale , number of sale for all the months in the current year, using
select monthname(date),sum(amt_c),MAX(amt_c)
from sales where year(date)= year(now())
group by monthname(date) ;
I want to also select the customer who has done the highest purchase , i.e the customer correponding to the MAX(amt_c).
amt_c is the purchase done by the customer,
One way is a filtering join:
select filter.mn
, filter.sum_sales
, filter.max_sales
, sales.cust
from (
select monthname(date) as mn
, sum(amt_c) as sum_sales
, max(amt_c) as max_sales
from sales
where year(date) = year(now())
group by
mn
) filter
join sales
on monthname(sales.date) = filter.mn
and sales.amt_c = filter.max_sales
For more approaches, browse the greatest-n-per-group tag.
select v.monthname,
v.sum_amt_c,
v.max_amt_c,
count(s.amt_c) as num_of_amounts,
group_concat(s.cust) as customers
from (select monthname(date) as monthname,
sum(amt_c) as sum_amt_c,
max(amt_c) as max_amt_c
from sales
where date between concat(year(now()), '-01-01') and concat(year(now()), '-12-31')
group by monthname(date)) v
join sales s
on v.max_amt_c = s.amt_c
and v.monthname = monthname(s.date)
and s.date between concat(year(now()), '-01-01') and concat(year(now()), '-12-31')
group by v.monthname, v.sum_amt_c, v.max_amt_c
order by month(s.date)
This is similar to Andomar's answer however it provides the following benefits:
If your DATE field is indexed (it should be) the above query will use that index. You should not have criteria on a date field with a function applied to it. MySQL does not support function based indexes, so it is a given that year(date) is not indexed. date may be indexed, however.
This sorts the results by month # (1-12) but shows the month name.
In the event that the same 2+ customers are tied, this will list all of them, and show only one row for that month. You would otherwise potentially have 2, 3, 4+ rows for a single month in the event of a tie. This is done via MySQL's GROUP_CONCAT function.

I need assistance with a query using group by with rollup

I am trying to display the book order data showing the year of the order in the first column, the month in the second. Plus, display totals by month and by year and grand totals. Also, display messages "Yearly Total" and "Grand Total" instead of the nulls. The result is sorted by the year and month.
I keep receiving an error (Unknown column 'order_date' in 'field list') can anyone help me?
select coalesce(year(order_date), 'Grand Total') as Year
, case when year(order_date) is null then ' ' else coalesce(month(order_date), 'Year Total') end as
Month
, AmntDue
, NumberOfBooksPurch
from (
select year(order_date) as Year
, month(order_date) as Month
, sum(quantity * order_price) as AmntDue
, count(order_id) as NumberOfBooksPurch
from a_bkorders.order_headers
join a_bkorders.order_details using (order_id)
group by year(order_date), month(order_date), order_id with rollup
) tbl;
order_date is a value in the original table, but it's not being returned by the subquery so you can't reference it in the outer query. Use the aliases that the subquery returns:
select coalesce(Year, 'Grand Total') as Year
, case when Year is null then ' ' else coalesce(Month, 'Year Total') end as
Month
, AmntDue
, NumberOfBooksPurch
from (
select year(order_date) as Year
, month(order_date) as Month
, sum(quantity * order_price) as AmntDue
, count(order_id) as NumberOfBooksPurch
from a_bkorders.order_headers
join a_bkorders.order_details using (order_id)
group by Year, Month, order_id with rollup
) tbl;