do rank order/decile in mysql with partition - mysql

I have multiple groups of cities in my dataset and I am trying to rank order price for each of those groups in mysql. Can someone help me convert the partition clause to mysql?

I am presuming that you are looking for the equivalent of rank() over (partition by city order by price). You can do this with a subquery:
select d.*,
(select 1 + count(price)
from dataset d2
where d2.city = d.city and d2.price < d.price
) as rank
from dataset d;
Or using variables:
select d.*,
(#rn := if(#city = city and #price > price, if(#price = #price, #rn + 1, #rn + 1),
if(#city = city and #price = price, #rn,
if(#city := city, if(#price := price, 1, 1), 1)
)
)
) as rank
from dataset d cross join
(select #rn := 0, #city := '', #price = -1)
order by city, price;

Related

How to find median value with group by (MySQL)

Need to find median value of time difference between sent date and click date (in seconds) for each type of emails. I found solution just for all data:
SET #rowindex := -1;
SELECT g.type, g.time_diff
FROM
(SELECT #rowindex:=#rowindex + 1 AS rowindex,
TIMESTAMPDIFF(SECOND, emails_sent.date_sent, emails_clicks.date_click) AS time_diff,
emails_sent.id_type AS type
FROM emails_sent inner join emails_clicks on emails_sent.id = emails_clicks.id_email
ORDER BY time_diff) AS g
WHERE g.rowindex IN (FLOOR(#rowindex / 2) , CEIL(#rowindex / 2));
Is it possible to add group by id_type statement?
Thanks!
First, you need to enumerate the rows for each type. Using variables, this code looks like:
select sc.*,
(#rn := if(#t = id_type, #rn + 1,
if(#t := id_type, 1, 1)
)
) as seqnum
from (select timestampdiff(second, s.date_sent, c.date_click) as time_diff,
s.id_type,
from emails_sent s inner join
emails_clicks c
on s.id = c.id_email
order by time_diff
) sc cross join
(select #t := -1, #rn := 0) as params;
Then, you need to bring in the total number for each type and do the calculation for the median:
select sc.id_type, avg(time_diff)
from (select sc.*,
(#rn := if(#t = id_type, #rn + 1,
if(#t := id_type, 1, 1)
)
) as seqnum
from (select timestampdiff(second, s.date_sent, c.date_click) as time_diff,
s.id_type,
from emails_sent s inner join
emails_clicks c
on s.id = c.id_email
order by time_diff
) sc cross join
(select #t := -1, #rn := 0) as params
) sc join
(select id_type, count(*) as cnt
from emails_sent s inner join
emails_clicks c
on s.id = c.id_email
group by id_type
) n
where 2 * seqnum in (n.cnt, n.cnt, n.cnt + 1, n.cnt + 2)
group by sc.id_type;

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;

mysql get last 10 records from each group

I have mysql table called ware_stock_transaction and it has order_no, order_type, created_date, item_no.
I want to get the last 10 record from each item, like this:
item A (10 records)
item B (10 records)
item C (10 records)
In MySQL, you can use variables:
select wst.*
from (select wst.*,
(#rn := if(#i = item_no, #rn + 1,
if(#i := item_no, 1, 1)
)
) as rn
from ware_stock_transaction wst cross join
(select #rn := 0, #i := '') params
order by item_no, created_date desc
) wst
where rn <= 10;

Mysql join with limits on joined table

I am trying to return a limited of number of products per brand. The tables are
brands:
id, name
products:
id, brand_id, name
What I am trying to achieve is a query that will output brands.name and products_name 10 times for each brand.
I have tried joining the two tables but when it comes to applying the limit I can't see the next step. Is this possible or will I have to opt to do the brand query first and then query again on a foreach this being more processor intensive?
Get 10 records per product from the second table by the following query:
SELECT *
FROM(
SELECT id, brand_id, name, #n := IF(#r = brand_id, #n + 1, 1) AS rownum,
#r := brand_id
FROM product, (SELECT #r := 0, #n := 0) a
ORDER BY brand_id, id
) a
WHERE a.rownum <= 10;
And join it with brand table, e.g.:
SELECT *
FROM brand b
JOIN (SELECT *
FROM(
SELECT id, brand_id, name, #n := IF(#r = brand_id, #n + 1, 1) AS rownum,
#r := brand_id
FROM product, (SELECT #r := 0, #n := 0) a
ORDER BY brand_id, id
) a
WHERE a.rownum <= 10
) p on b.id = p.brand_id;
Here's the SQL Fiddle.

Getting the rank of a record using two where clauses

I am trying to get the rank of specified record, and I have some success with this code:
SELECT `rank`
FROM
(
select #rownum:=#rownum+1 `rank`, p.*
from TableName p, (SELECT #rownum:=0) r
order by point DESC
) s
WHERE names = 'house'
(See the schema here.)
This SQL query works, but if I want to get the result according to city_id and name, I must use two where clauses, and then the code doesn't work.
I want to get rank of house only for its city. How can I do this?
I want to get rank of "house" only for its city
You can do this by introducing another variable to keep track of the city:
SELECT `rank`
FROM (select p.*,
(#rn := if(#c = city, #rn + 1,
if(#c := city, 1, 1)
)
) as rank
from TableName p CROSS JOIN
(SELECT #rn := 0, #c := -1) params
order by city_id, point DESC
) s
WHERE names = 'house' ;
You can also use your original query, with a tweak, if you know that "house" only appears once in the data:
SELECT `rank`
FROM (select p.*, (#rn := #rn + 1) as rank
from TableName p CROSS JOIN
(SELECT #rn := 0) params
where city_id = (select city_id from TableName t2 where t2.names = 'house')
order by point DESC
) s
WHERE names = 'house' ;