Aggregate Functions in MySQL - mysql

I have been running into some issues with where/when I can use aggregate functions in MySQL. If I have the following two simple tables:
Campaign Table (campaign_id, campagin_name, account, country)
Revenue Table (campaign_id, revenue, date)
I want to write a query to find the top account by revenue for each week:
I tried the following
SELECT account, SUM(revenue) as sum_rev
FROM campaign
JOIN revenue
ON c.campaign_id = r.campaign_id
WHERE revenue =
( SELECT revenue
FROM campaign
JOIN revenue
ON c.campaign_id = r.campaign_id
WHERE revenue = MAX(SUM(revenue))
)
GROUP BY week(date)
I was told this isn't correct, is the issue just the nesting of the aggregate function max and sum?

In MySQL, I think variables are the simplest way:
SELECT cr.*
FROM (SELECT cr.*,
(#rn := if(#w = concat_ws('-', yyyy, wk), #rn + 1,
if(#rn := concat_ws('-', yyyy, wk), 1, 1)
)
) as rn
FROM (SELECT c.account, year(r.date) as yyyy, week(r.date) as wk, SUM(r.revenue) as sum_rev
FROM campaign c JOIN
revenue r
ON c.campaign_id = r.campaign_id
GROUP BY c.account, year(r.date), week(r.date)
ORDER BY yyyy, wk, sum_rev DESC
) cr CROSS JOIN
(SELECT #wy := '', #rn := 0) params
) cr
WHERE rn = 1;

Related

How to get top selling item every month

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

user defined variable to store ranking gives wrong values if order by

mysql table: work
|id|user_id|created_at|realization|
I have been working on a sql query which calculates performance (realisation today / realization on the first day of the month) and sortes records based on performance.
Expected result:
|ranking|performance|user|
|1|0.88|36|
|2|0.712444111|444|
|3|0.711|1|
|4|0.33333|9|
|5|0.1006|29|
returned result:
|ranking|performance|user|
|4|0.88|36|
|2|0.712444111|444|
|5|0.711|1|
|3|0.33333|9|
|1|0.1006|29|
Here is my query:
SET #ranking := 0;
SELECT
#ranking := #ranking + 1 as ranking,
w1.user_id,
IFNULL(ROUND(w2.realization / w1.realization), 4), 0) AS performance
FROM work w1
JOIN (
SELECT min(created_at) AS first_month, max(created_at) AS last_month, user_id
FROM work
WHERE (DATE_FOMAT(NOW(), '%Y-%m') = DATE_FORMAT(created_at, '%Y-%m')
GROUP BY user_id
ORDER BY user_id
) AS w ON w1.user_id = w.user_id AND w1.created_at = w.first_month
JOIN work AS w2 ON w1.user_id = w2.user_id AND w2.created_at = w.last_month
ORDER BY performance DESC
UPDATE
Even if I try to wrap it this way, the rankings are not right
SET #ranking := 0;
SELECT #ranking := #ranking + 1 as ranking, a.user_id, a.performance
FROM (
SELECT
w1.user_id,
IFNULL(ROUND(w2.realization / w1.realization), 4), 0) AS performance
FROM work w1
JOIN (
SELECT min(created_at) AS first_month, max(created_at) AS last_month, user_id
FROM work
WHERE (DATE_FOMAT(NOW(), '%Y-%m') = DATE_FORMAT(created_at, '%Y- %m')
GROUP BY user_id
ORDER BY user_id
) AS w ON w1.user_id = w.user_id AND w1.created_at = w.first_month
JOIN work AS w2 ON w1.user_id = w2.user_id AND w2.created_at = w.last_month
ORDER BY performance DESC
) AS a

MySQL netting of rows based on available stock

I have a MySQL database that includes a table of orders and table of part numbers with available stock.
For example
==Orders Table==
part_number
order_date
qty
==Stock Table==
part_number
stock_qty
Assuming that available stock will be assigned to the most recent orders, how would I select rows from the orders table that don't have stock to cover them.
The orders table could have between 1.5 and 2 million records so reading the whole data set into memory and processing in PHP first is not ideal.
===Update and Final Answer===
Gordon's answer gave me enough information to get the exact result that worked for me. which I have included below.
select * from (
select o.*,s.stock_qty,(#netted_stock := if(#pn = o.part_number,#netted_stock-sumqty,s.stock_qty-sumqty)) as netted_stock
from (select o.*,
(#sumqty := if(#pt = part_number, #sumqty + qty,
if(#pt := part_number, qty, qty)
)
) as sumqty
from orders o cross join
(select #pn := -1, #sumqty := 0, #netted_stock:= 0) params
order by part_number, order_date desc
) o join
stock s
on o.part_number = s.part_number
) s
Here is the result it gives me.
You need a cumulative sum of stock per order. You can get that with variables. The rest is just simple logic:
select o.*
from (select o.*,
(#sumqty := if(#pt = part_number, #sumqty + qty,
if(#pt := part_number, qty, qty)
)
) as sumqty
from orders o cross join
(select #pn := -1, #sumqty := 0) params
order by part_number, order_date desc
) o join
stock s
on o.part_number = s.part_number
where s.stock_qty < o.sumqty;

mysql query to get the average from the latest N rows for distinct phone_numbers

My table is (phone_number, bpm, timestamp). I want to get the phone_number, avg(bpm) for the latest 10 rows for each phone_number. I have tried..
SELECT phone_number, AVG( bpm )
FROM (
SELECT *
FROM table_name
WHERE bpm !=0
ORDER BY timestamp DESC
) AS temp
GROUP BY phone_number
HAVING COUNT( * ) <=10
This query is giving empty result. I cannot use IN clause in my version of mysql.
This is a pain in MySQL. The most reasonable solution uses variables to enumerate the rows and then aggregation:
SELECT phone_number, AVG(bpm)
FROM (SELECT t.*,
(#rn := if(#pn = phone_number, #rn + 1,
if(#pn := phone_number, 1, 1)
)
) as rn
FROM table_name t CROSS JOIN
(SELECT #pn := '', #rn := 0) params
WHERE bpm <> 0
ORDER BY phone_number, timestamp DESC
) t
WHERE rn <= 10
GROUP BY phone_number;

Finding the most rented car of each year in SQL

I have tables as follows:
Car(id, make, ....)
Deal(id,datetime,car_id,....)
I want to write a query that would return a year, and a car make for the cars that have the most deals (ie the most deal ids) and the number of deals for that car make.
I started out,
SELECT YEAR(D.datetime) AS the_year, C.make, COUNT(D.id) AS num
FROM Deal D, Car C
WHERE D.car_id=C.id
GROUP BY the_year
Unfortunately, this has returned the year and the total number of deals.
So I am thinking to create this within another table and then call MAX(tbl.num), but I am confused on the syntax.
Can somebody help me out please?
This is an interesting problem. What you are looking for is specifically called the "mode" in statistics. In MySQL, you would get this by using variables or the group_conat()/substring_index()` trick. I'll show the latter:
SELECT the_year,
substring_index(group_concat(cd.make order by num desc), ',', 1) as the_mark
FROM (SELECT YEAR(D.datetime) AS the_year, C.make, COUNT(D.id) AS num
FROM Deal D JOIN
Car C
ON D.car_id = C.id
GROUP BY the_year, c.make
) cd
GROUP BY the_year;
EDIT:
The version using variables:
SELECT the_year,
substring_index(group_concat(cd.make order by num desc), ',', 1) as the_mark
FROM (SELECT YEAR(D.datetime) AS the_year, C.make, COUNT(D.id) AS num,
#rn := if(#year = YEAR(D.datetime), #rn + 1, 1) as rn,
#year := YEAR(D.datetime)
FROM Deal D JOIN
Car C
ON D.car_id = C.id CROSS JOIN
(SELECT #year := 0, #rn := 0) vars
GROUP BY the_year, c.make
ORDER BY the_year, num DESC
) cd
WHERE rn = 1;