Mysql selecting top 10 of each category analytic function - mysql

I have a table which has the following two columns including others: rating, price and code. Ratings is similar to a category. I wish to generate the top 10 for each rating order by price ascending where code = 'ABC'. I'm at a loss to figure out where to put the last two conditions in the following mysql statement. Please can someone advise. Many thanks
SELECT x.*
FROM (SELECT t.*,
CASE
WHEN #rating != t.rating THEN #rownum := 1
ELSE #rownum := #rownum + 1
END AS rank,
#rating := t.rating AS var_rating
FROM offers t
JOIN (SELECT #rownum := NULL, #rating := '') r
ORDER BY t.rating) x
WHERE x.rank <= 10
ALso, what if the rating column had entries like 1, 1, 1k, 1*, 1+, 2, 2, 2+, 3,3,3* etc, how would I be able to consider all of these entries as '1', 2 and 3 respectively in the same sql statement?

Try this:
SELECT x.*
FROM (SELECT t.*,
CASE
WHEN #rating != t.rating THEN #rownum := 1
ELSE #rownum := #rownum + 1
END AS rank,
#rating := t.rating AS var_rating
FROM offers t
JOIN (SELECT #rownum := NULL, #rating := '') r
WHERE code = 'ABC'
ORDER BY t.rating, price) x
WHERE x.rank <= 10
The changes are:
Added a WHERE clause after the JOIN clause
Added price to the ORDER BY clause.

Related

Why doesn´t it work the other way arround? SQL

so I have this command:
SELECT t.Character_ID,
#rownum := #rownum + 1 AS rank
FROM characters t,
(SELECT #rownum := 0) r
Now my table has an extra rows with the number starting from 1. Next I extend the command with a WHERE command and everything works fine:
SELECT t.Character_ID,
#rownum := #rownum + 1 AS rank
FROM characters t,
(SELECT #rownum := 0) r WHERE #rownum < 5
And now I would like to extend the command as follows:
SELECT t.Character_ID,
#rownum := #rownum + 1 AS rank
FROM characters t,
(SELECT #rownum := 0) r WHERE #rownum > 5
or
SELECT t.Character_ID,
#rownum := #rownum + 1 AS rank
FROM characters t,
(SELECT #rownum := 0) r WHERE #rownum BETWEEN 5 AND 10;
And this doesnt work and i dont know why. I doesn´t get an error or something but I got nothing as a return just an empty table. Does anyone know why?
You must use your query as a subquery and then filter:
SELECT t.*
FROM (
SELECT t.Character_ID,
#rownum := #rownum + 1 AS rank
FROM characters t, (SELECT #rownum := 0) r
) t
WHERE t.rank > 5
In the order of operations the where comes before the select - so your first query works but the other 2 don't. fix by wrapping in main query where you can test row number, search for sql order of operations for articles on this topic.
One method uses offset/limit:
SELECT t.Character_ID,
#rownum := #rownum + 1 AS rank
FROM characters t CROSS JOIN
(SELECT #rownum := 0) r
LIMIT 5 OFFSET 99999999;
Another method uses a having clause:
SELECT t.Character_ID,
#rownum := #rownum + 1 AS rank
FROM characters t CROSS JOIN
(SELECT #rownum := 0) r
HAVING rank > 5;
No subquery is needed.
Two more things. SQL tables represent unordered sets, so ORDER BY is almost always used in such a query. Second, you should also know that variables assignments in SELECT statements are deprecated. You should learn to use window functions.

Ranking order with having same rank repeating to the same level rank category

I am little new to sql and I need to rank my votes table based on votes assigned to each person and rank should be same for the similar votes.
My table would be like
CREATE TABLE votes ( name varchar(10), votes INT );
INSERT INTO votes VALUES
('Ann',100), ('Jones',151), ('Smith',100), ('Rose',240), ('Lee',500), ('Adhams',500);
In my display rows I need to have the rank column first and it should display the rank based on the highest number of votes. Importantly same number of votes need to have the same rank.
I have tried it several times and failed to do it...
Please help me
thanks
You can try below code. There are many links available which would be easily find your answer if you have carefully searched.
SET #rank=0;
SET #votes=0;
select x.rank as rank, x.name as name, x.votes as votes
from(
select #rank:=if(#votes=votes,#rank, #rank +1)AS rank,
#votes := votes,
v.name,
v.votes
from votes v
order by v.votes desc) as x;
To generate RANK, you first need a row number in the order of decreasing votes (variable #rn) and then based on the previous value of vote, create rank (variable #rank).
Try this:
SELECT v.*,
#rank := if((#rn := #rn + 1) is not null,
if (#votes = votes,
#rank,
if ((#votes := votes) is not null, #rn, 1)
),1
) rank
FROM votes v
CROSS JOIN (
SELECT
#votes := NULL,
#rank := 0,
#rn := 0
) t
ORDER BY v.votes DESC;
Demo
About this:
(#rn := #rn + 1) is not null
Since, the expression #rn := #rn + 1 can't be null, we use it to our advantage by not duplicating the whole logic twice.
You can use variables for this:
SELECT #rnk := IF(#v = votes, #rnk,
IF(#v := votes, #rnk + 1, #rnk + 1) AS rnk
name, votes
FROM mytable
CROSS JOIN (SELECT #rnk := 0, #v = :0) AS vars
ORDER BY votes DESC

MySQL get rank from particular row ID

I have list of hospitals under that there are average ratings already calculated. Now I wanted to calculate rank for list of hospitals according to their average ratings from following query
SELECT name,
hospitalID,
currentAvgRating,
#curRank := #curRank + 1 AS rank
FROM hospitals h, (SELECT #curRank := 0) r
ORDER BY currentAvgRating DESC
Now above query works when I want to see all hospitals from table but when I apply WHERE clause like below then result is wrong since with this it takes row position.
SELECT name,
hospitalID,
currentAvgRating,
#curRank := #curRank + 1 AS rank
FROM hospitals h, (SELECT #curRank := 0) r where hospitalID = '453085'
ORDER BY currentAvgRating DESC
Is there any way to get correct result when we apply where clause?
If you proceed what you just found out, logically ("when there is only 1 listitem, it cannot be ordered") - you will come to the conclusion that you NEED to select ALL rows. But nothing wrong with that, you can pack them into a subselect (which isnt even an expensive one) and apply the WHERE to that:
SELECT * FROM (
SELECT name,
hospitalID,
currentAvgRating,
#curRank := #curRank + 1 AS rank
FROM hospitals h, (SELECT #curRank := 0) r
ORDER BY currentAvgRating DESC
) toplist
WHERE toplist.hospitalID = 453085
Wrap in a subquery.
SELECT * FROM (
SELECT name,
hospitalID,
currentAvgRating,
#curRank := #curRank + 1 AS rank
FROM hospitals h, (SELECT #curRank := 0) r
ORDER BY currentAvgRating DESC
)
WHERE hospitalID = '453085'

MariaDB/MySQL RANK() implementation

I am working on migration from MS SQL Server to MariaDB 10.0. I have query which use RANK() PARTITION BY. I already created some kind of RANK() implementation on my table but it's not working properly.
The original query was:
RANK() OVER (PARTITION BY visits.id_partner ORDER BY visits.updated_at DESC) AS rank
My implementation for MariaDB/MySQL:
SELECT
...,
(
CASE visits.id_partner
WHEN #currId THEN
#curRow := #curRow + 1
ELSE
#curRow := 1 AND #currId := visits.id_partner
END
) AS rank
FROM
records rec
JOIN visits ON visits.id = rec.id_visit,
(
SELECT
#curRank := 0,
#currId := NULL
) r
WHERE
...
ORDER BY visits.id_partner ASC, visits.updated_at DESC
I want to select row ranked by id_partner order by updated_at field. When id_partner is same as on row before RANK should increase by 1. When is different than before, it should reset to 1.
But my query is not working at all. I have still rank 1 on all rows. Can you help me find mistake?
Thank you for help!
It is tricky to use variables in MySQL/MariaDB. A variable should only be used and assigned in one statement (as you do correctly). However, AND can short-circuit variable assignment.
I use a construct like this for ROW_NUMBER(). RANK() is actually a bit of a pain . . . DENSE_RANK() and ROW_NUMBER() are simpler. However, this seems to be the code that you are aiming for:
SELECT ...,
(#rn := if(#currId = visits.id_partner, #rn + 1,
if(#currId := visits.id_partner, 1, 1)
)
) as rank
FROM records rec JOIN
visits
ON visits.id = rec.id_visit CROSS JOIN
(SELECT #rn := 0, #currId := NULL) params
WHERE
...
ORDER BY visits.id_partner ASC, visits.updated_at DESC;
EDIT:
In MySQL (and presumably in MariaDB), sometimes variables don't work quite right unless you use a subquery. So, try this:
SELECT . . .,
(#rn := if(#currId = visits.id_partner, #rn + 1,
if(#currId := visits.id_partner, 1, 1)
)
) as rank
FROM (SELECT ...
FROM records rec JOIN
visits
ON visits.id = rec.id_visit
WHERE
...
ORDER BY visits.id_partner ASC, visits.updated_at DESC
) t CROSS JOIN
(SELECT #rn := 0, #currId := NULL) params;

MySQL Ranking ID with Ties Based on Column Returns Error

I have a 1 table database with (in a simplified form) the following fields:
user_id(INT), ref_id(INT), points(INT), pointsgiven(BOOL/TINY_INT)
I want a query that returns the RANK of the user_id I specify based on points, given that pointsgiven is true. The kicker is, I need ties included. I can get a result set for ALL ranks if I want with the following query
SELECT
user_id, ref_id, points, pointsgiven,
CASE
WHEN #points = COALESCE(points, 0) THEN #rownum
ELSE #rownum := #rownum + 1
END AS rank,
#points := COALESCE(points, 0)
FROM users CT
JOIN
(
SELECT #rownum := 0, #points := NULL
) r
WHERE pointsgiven=TRUE ORDER BY points DESC
So, based on that, I thought I could just use that as a subquery to get a certain user_id as follows:
select * from
(
SELECT
user_id, ref_id, points, pointsgiven,
CASE
WHEN #points = COALESCE(points, 0) THEN #rownum
ELSE #rownum := #rownum + 1
END AS rank,
#points := COALESCE(points, 0)
FROM users CT
JOIN
(
SELECT #rownum := 0, #points := NULL
) r
WHERE pointsgiven=TRUE ORDER BY points DESC
) as derived WHERE user_id = 15
But this returns [BLOB - 1 B] as the rank on the correct user_id. What am I doing wrong here?
I have no idea why your query isn't working. For a single user id, though, you can use a correlated subquery:
select user_id, ref_id, points, pointsgiven,
coalesce((select count(distinct user_id)
from users u2
where u2.pointsgiven=TRUE and
u2.points > u.points
) + 1, 1) as rank
from users u
where user_id = 15;
An index on users(pointsgiven, points, user_id) should be used by the query.
To look at just one ranking, this might even be faster than your method.