How to get top selling item every month - mysql

I want to get top selling item/product for every month. I tried to using GROUP BY function, but my problem is how to get just 1 product in every month.
SELECT MONTHNAME(date), product, SUM(quantity)
FROM mytable
GROUP BY MONTHNAME(date), product
Also, how to use row_number() over function in mysql? I think by using that I can get just 1 product per month?
This is what I want to get:

You can try below -
select MONTHNAME(`date`),product,sum(quantity) qty
from tablename a
group by MONTHNAME(`date`),product
having max(qty) in (select sum(quantity) from tablename b where MONTHNAME(a.`date`)=MONTHNAME(b.`date`) and a.product=b.product)

SELECT
month, n, product, quantity
FROM
( SELECT #prev := '', #n := 0 ) init
JOIN
( SELECT #n := if(MONTHNAME(date) != #prev, 1, #n + 1) AS n,
#prev := MONTHNAME(date),
MONTHNAME(date) AS month, product, SUM(quantity) AS quantity
FROM mytable
GROUP BY
MONTHNAME(date), product
ORDER BY
MONTHNAME(date) ASC,
SUM(quantity) DESC
) x
WHERE n <= 1
ORDER BY month, n

Related

Rank each employee based on their merit points from highest to lowest

Create two tables emp_merits (meritid, empid, date, meritpoints),emp1(empid,empname) Each employee will be given merit points every month based on their performance. So same employee can have multiple entries in the table with different meritpoints.
List all the merits received by a specific employee (empid will be input here) between specific dates
Rank each employee based on their merit points from highest to lowest
so far i have tried this query
select empid , sum (meritpoints) as totalmerits , (DENSE_RANK()OVER (PARTITION BY empid ORDER BY meritpoints desc)) AS rank from emp_merit
group by empid,meritpoints
order by empid ,totalmerits desc
You could try this:
SELECT #rownum := #rownum + 1 AS rank, a.*
FROM (
SELECT empid, sum(meritpoints) AS totalmerits
FROM emp_merits
GROUP BY empid
ORDER BY totalmerits) a, (SELECT #rownum := 0) r ;
you probably need your specific dates in the WHERE-clause.
You can implement dense_rank() using variables:
select empid, totalmerits,
(#rn := if(#m = totalmerits, #rn,
if(#m := totalmerits, #rn + 1, #rn + 1)
)
) as rank
from (select empid, sum(meritpoints) as totalmerits
from emp_merit
group by empid
order by totalmerits desc
) e cross join
(select #m := -1, #rn := 0) params;

Select multiple top3 from database

I would like to create a database view. I have got table, which contains records about the peoples, which solved some logical examples during year and they got points for that.
I need to create view from SQL query, but I have got a problem - I need to get TOP 3 users per every year by one query.
SELECT solver, year, SUM(points) as TotalPoints
FROM solving
GROUP BY solver, year
ORDER BY year, TotalPoints DESC
I got solvers per years sorted by points and year. I know I need to use limit, but I also know I will need one Select more, but I dont know where.
There are several ways to solve this in MySQL. Probably the most efficient is to use variables. The following enumerates the values:
SELECT yt.*,
(#rn := if(#y = year, #rn + 1,
if(#y := year, 1, 1)
)
) as seqnum
FROM (SELECT solver, year, SUM(points) as TotalPoints
FROM solving
GROUP BY solver, year
) yt CROSS JOIN
(SELECT #y := 0, #rn := 0) vars
ORDER BY year, TotalPoints DESC;
Then use this as a subquery to get the top three:
SELECT yt.*
FROM (SELECT yt.*,
(#rn := if(#y = year, #rn + 1,
if(#y := year, 1, 1)
)
) as seqnum
FROM (SELECT solver, year, SUM(points) as TotalPoints
FROM solving
GROUP BY solver, year
) yt CROSS JOIN
(SELECT #y := 0, #rn := 0) vars
ORDER BY year, TotalPoints DESC
) yt
WHERE seqnum <= 3;
this may help you out.
SELECT solver,
year,
SUM(points) AS TotalPoints
FROM (
SELECT solver,
year,
SUM(points) AS TotalPoints
FROM solving
ORDER BY year,
TotalPoints DESC
)
WHERE rownum <= 3
ORDER BY year,
TotalPoints DESC;

To display top 4 rows using mysql Rank is displaying wrong

I need to display the top4 and lease 4 rows based Amount and group by agentId but here rank is showing wrong
And how to show least(last 4 rows?)
schema:
AgentID amount
1 3000
1 3200
2 9000
SELECT Agentid,SUM(AmountRecevied) as Amount,#rownum := #rownum + 1 AS Rank
FROM collection ,(SELECT #rownum := 0) r
GROUP BY AgentID
ORDER BY Amount DESC
limit 4;
Try this way:
SELECT T.Agentid,T.Amount, #rownum := #rownum - 1 AS Rank
FROM
(SELECT Agentid,SUM(AmountRecevied) as Amount
FROM collection
GROUP BY AgentID
ORDER BY Amount
LIMIT 4) T,(SELECT #rownum := 11) r
Try this :
SELECT
C.*,
#rownum := #rownum + 1 AS Rank
FROM (
SELECT
Agentid,
SUM(AmountRecevied) as Amount
FROM collection
GROUP BY AgentID
ORDER BY Amount DESC
LIMIT 4
) AS C, (SELECT #rownum := 0) r
In case of amount matching for different agentids, then, I believe, ranks should be assigned same.
This solution should help you:
select
/*case when rank>6 then '' else rank end as */
rank, agentid, amount
from (
select agentid, #ca:=amount amount
, case when #pa=#ca then #rn:=#rn
else #rn:=( #rn + 1 )
end as rank
, #pa:=#ca as temp_currAmount
from ( select agentid, sum(amount) as amount
from agents
group by agentid
order by amount
) amounts_summary,
(select #pa:=0, #c0:=0,
#rn:=0) row_nums
order by rank desc
) results
where rank > 6
order by rank
;
Demo # MySQL 5.6.6 Fiddle
And if you want no display ranks greater than '6' but empty, then
just uncomment the case line and comment the where condition line
select
case when rank>6 then '' else rank end as
rank, agentid, amount
from (
select agentid, #ca:=amount amount
, case when #pa=#ca then #rn:=#rn
else #rn:=( #rn + 1 )
end as rank
, #pa:=#ca as temp_currAmount
from ( select agentid, sum(amount) as amount
from agents
group by agentid
order by amount
) amounts_summary,
(select #pa:=0, #ca:=0,
#rn:=0) row_nums
order by rank
) results
-- where rank > 6
order by rank
;
You can modify asc or desc as required.
Demo # MySQL 5.6.6 Fiddle

MySql Ranking users

I am using this code to rank users:
SELECT #rn:=#rn+1 AS rank, userid, amount
FROM (
SELECT userid, sum(amount) AS amount
FROM leads WHERE date(time)='2013-09-15'
GROUP BY userid
ORDER BY amount DESC
) t1 , (SELECT #rn:=0) t2;
The result is like this:
rank userid amount
1 11 1.15
2 10 1.15
It keeps adding rank even if the user has the same amount, any ideas how to fix this? Yes, I have searched google and here on stackoverflow, but I have not been able to fix this problem.
First, you don't need a subquery to do what you want.
The following does a dense ranking of the amounts, by introducing another variable to remember the total amount:
SELECT userid, sum(amount) AS amount,
if(#amount = amount, #rn, #rn := #rn + 1) as ranking,
#amount := amount
FROM leads cross join
(select #rn := 0, #amount := -1) const
WHERE date(time) = '2013-09-15'
GROUP BY userid
ORDER BY amount DESC;

Cumulative count over time

I have a table orders like this:
customer_id order_date
10 2012-01-01
11 2012-01-02
10 2012-01-02
12 2012-01-03
11 2012-01-04
12 2012-02-01
11 2012-02-04
13 2012-02-05
14 2012-02-06
How can I get a cumulative average over time (per month) like this:
order date count orders count customers (customer_id)
2012-01 1 1 (12)
2012-01 2 2 (10,11)
2012-02 1 2 (13,14)
2012-02 2 2 (10,12
2012-02 3 2 (11)
showing how the number of customers vs. number of orders per customer develops over time.
The following query gives me the wanted information - but not over time. How can I iterate the query over time?
SELECT number_of_orders, count(*) as amount FROM (
SELECT o.customer_id, count(*) as number_of_orders
FROM orders o
GROUP BY o.customer_id) as t1
GROUP BY number_of_orders
Update:
have now build the following PHP code to generate what I need, wonder if that could be done using cumulative counts like on http://www.freeopenbook.com/mysqlcookbook/mysqlckbk-chp-12-sect-14.html
$year = 2011;
for ($cnt_months = 1; $cnt_months <= 12; $cnt_months++) {
$cnt_months_str = ($cnt_months < 10) ? '0'.$cnt_months : $cnt_months;
$raw_query = "SELECT number_of_orders, count(*) as amount
FROM (
SELECT
o.customer_id,
count(*) as number_of_orders
FROM orders o
where Date_Format( o.order_date, '%Y%m' ) >= " . $year . "01 and Date_Format( o.order_date, '%Y%m' ) <= " . $year . $cnt_months_str . "
GROUP BY o.customer_id) as t1
GROUP BY number_of_orders";
$query = db_query($raw_query);
while ($row = db_fetch_array($query)) {
$data[$cnt_months_str][$row['number_of_orders']] = array($row['number_of_orders'], (int)$row['amount']);
}
}
A good starting point is
SELECT
order_date,
COUNT(*) AS distinctOrders,
COUNT(DISTINCT customer_id) AS distinctCustomers,
GROUP_CONCAT(DISTINCT customer_id ASC) AS customerIDs
FROM orders
GROUP BY order_date ASC
This will give you the order_date, the number of orders on that date, the number of customers on that date, and the list of customer ids on that date.
Just looking at a way to tally up on a month by month basis. So taking this forward I've used a subquery to tally up as it goes
SELECT
ordersPerDate.*,
IF(
MONTH(ordersPerDate.order_date)=#thisMonth,
#runningTotal := #runningTotal+ordersPerDate.distinctOrders,
#runningTotal := 0
) AS ordersInThisMonth,
#thisMonth := MONTH(ordersPerDate.order_date)
FROM
(
SELECT
#thisMonth := 0,
#runningTotal := 0
) AS variableInit,
(
SELECT
order_date,
COUNT(*) AS distinctOrders,
COUNT(DISTINCT customer_id) AS distinctCustomers,
GROUP_CONCAT(DISTINCT customer_id ASC) AS customerIDs
FROM orders
GROUP BY order_date ASC
) AS ordersPerDate
And finally to clean it up, wrapped it in yet another subquery just to return the rows desired rather than the internal variables
Grouping on individual days
SELECT
collatedData.order_date,
collatedData.ordersInThisMonth AS count_orders,
collatedData.distinctCustomers AS count_customers,
collatedData.customerIDs AS customer_ids
FROM (
SELECT
ordersPerDate.*,
IF(
MONTH(ordersPerDate.order_date)=#thisMonth,
#runningTotal := #runningTotal+ordersPerDate.distinctOrders,
#runningTotal := 0
) AS ordersInThisMonth,
#thisMonth := MONTH(ordersPerDate.order_date)
FROM
(
SELECT
#thisMonth := 0,
#runningTotal := 0
) AS variableInit,
(
SELECT
order_date,
COUNT(*) AS distinctOrders,
COUNT(DISTINCT customer_id) AS distinctCustomers,
GROUP_CONCAT(DISTINCT customer_id) AS customerIDs
FROM orders
GROUP BY order_date ASC
) AS ordersPerDate
) AS collatedData
And now finally, following additional information from the OP, the end product
Grouping on calendar months
// Top level will sanitise the output
SELECT
collatedData.orderYear,
collatedData.orderMonth,
collatedData.distinctOrders,
collatedData.ordersInThisMonth AS count_orders,
collatedData.distinctCustomers AS count_customers,
collatedData.customerIDs AS customer_ids
FROM (
// This level up will iterate through calculating running totals
SELECT
ordersPerDate.*,
IF(
(ordersPerDate.orderYear,ordersPerDate.orderMonth) = (#thisYear,#thisMonth),
#runningTotal := #runningTotal+ordersPerDate.distinctOrders*ordersPerDate.distinctCustomers,
#runningTotal := 0
) AS ordersInThisMonth,
#thisMonth := ordersPerDate.orderMonth,
#thisYear := ordersPerDate.orderYear
FROM
(
SELECT
#thisMonth := 0,
#thisYear := 0,
#runningTotal := 0
) AS variableInit,
(
// Next level up will collate this to get per year, month, and per number of orders
SELECT
ordersPerDatePerUser.orderYear,
ordersPerDatePerUser.orderMonth,
ordersPerDatePerUser.distinctOrders,
COUNT(DISTINCT ordersPerDatePerUser.customer_id) AS distinctCustomers,
GROUP_CONCAT(ordersPerDatePerUser.customer_id) AS customerIDs
FROM (
// Inner query will get the number of orders for each year, month, and customer
SELECT
YEAR(order_date) AS orderYear,
MONTH(order_date) AS orderMonth,
customer_id,
COUNT(*) AS distinctOrders
FROM orders
GROUP BY orderYear ASC, orderMonth ASC, customer_id ASC
) AS ordersPerDatePerUser
GROUP BY
ordersPerDatePerUser.orderYear ASC,
ordersPerDatePerUser.orderMonth ASC,
ordersPerDatePerUser.distinctOrders DESC
) AS ordersPerDate
) AS collatedData
SELECT
substr(order_date,1,7) AS order_period,
count(*) AS number_of_orders,
count(DISTINCT orders.customer_id) AS number_of_customers,
GROUP_CONCAT(DISTINCT orders.customer_id) AS customers
FROM orders
GROUP BY substr(order_date,1,7)