MySql Get top n in each group - mysql

I wrote a query which is like :
select title, userId, created_at, deviceCode, streamTimeInSecond
from ( select title, userId, created_at, deviceCode, streamTimeInSecond,
#userId_rank:=IF(#current_userId = userId, #userId_rank + 1, 1) as userId_rank,
#current_userId:=userId
from ViewforfirstfiveMovies order by userId, streamTimeInSecond desc ) ranked
where userId_rank<=5;
In this query I am trying to get 5 titles viewed by each userId present in database.
Problem: I am getting more than 5 records for few users.
Kindly help me with this problem.

MySQL does not guarantee the order of evaluation of expressions in the select. For that reason, you should only set inter-related variables in a single expression.
I would write this as:
select title, userId, created_at, deviceCode, streamTimeInSecond
from (select title, userId, created_at, deviceCode, streamTimeInSecond,
(#userId_rank := if(#current_userId = userId, #userId_rank + 1,
if(#current_userId := userId, 1, 1)
)
) as userId_rank,
from ViewforfirstfiveMovies cross join
(select #current_userId := 0, #userId_rank := 0) params
order by userId, streamTimeInSecond desc
) ranked
where userId_rank <= 5;
In addition, you should set the variables in the same statement.

Related

A view with internal variables

I have a query where I want to fetch the top x for each value of another column. For doing this I have the following query
SELECT *,
(#num := IF(#job = job_id, #num + 1, IF(#job := job_id, 1, 1))) row_number
FROM job_user_rank t
CROSS JOIN (SELECT #num := 0, #job := null) c
where is_match = 1
ORDER BY job_id, is_match DESC, rank DESC
I then wrap this query and add a where row_number <= ?, however I want to make that inner query into a view instead, but I get the following error [HY000][1351] View's SELECT contains a variable or parameter, how can I get around that?

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;

how can i group by field value?

how can i group by one field start by value 0
eg.
select * from t;
id, check_id, user_name
1, 0, user_a
2, 1, user_a
3, 2, user_a
1, 0, user_a
2, 1, user_a
3, 3, user_a
1, 0, user_b
2, 1, user_b
3, 3, user_b
group by check_id by start by value 0 per group
user_name, check_info
user_a, 0-1-2
user_a, 0-1-3
user_b, 0-1-3
how can i group by?
Well, i read in the question : group by one field start by value 0
Then, you can try this.
select user_name,group_concat(distinct check_id order by check_id asc separator '-') check_info
from (
select id,check_id,user_name,
case when check_id = 0 then
#rn := #rn+1
else
#rn := #rn
end as unique_id
from t
inner join (select #rn := 0) as tmp
order by user_name
) as tbl
group by user_name,unique_id
This will group by for every records start by 0 and order by user_name.
This will give you what you want....maybe. It does work but is relying on the records coming back in the appropriate order when selected from the table (and that is NOT certain to occur).
SELECT user_name, GROUP_CONCAT(check_id ORDER BY grouping, check_id SEPARATOR '-')
FROM
(
SELECT id, check_id, user_name, #grouping:=if(id > #prev_id, #grouping, #grouping + 1) AS grouping, #prev_id:=id
FROM t
CROSS JOIN
(
SELECT #grouping:=0, #prev_id:=0
) sub0
) sub1
GROUP BY user_name, grouping
It works by returning the rows and using variables to assign a grouping to them (so when the id gets smaller it adds one to the grouping value), then does a GROUP BY on the user name and the grouping value.
But really you need to have the grouping value somehow stored with your data in advance.
Provided that id is an auto-increment field, then you can use:
SELECT user_name,
GROUP_CONCAT(check_id ORDER BY check_id SEPARATOR '-') AS check_info
FROM (
SELECT id, check_id, user_name,
#grp := IF (#uname = user_name,
IF (check_id = 0, #grp + 1, #grp),
IF (#uname := user_name, #grp + 1, #grp + 1)) AS grp
FROM mytable
CROSS JOIN (SELECT #grp := 0, #uname := '') AS vars
ORDER BY id) AS t
GROUP BY user_name, grp
Variables are used to identify slices of consecutive records, within each user_name partition, starting by 0.
Demo here

mysql rank - row number where field is equal to X

This is my mysql DB:
id, auth, name, points
and what I want to get is to create a rank. So sort all the records by points, and get the number of the row where the auth field is equal to '1'
I were looking for this in stockoverflow archive, in google etc. However, I havn't find what I were looking for. I were trying to do it myself, but none of them didn't work for me.
Could anyone help me, please?
SELECT a.iterator, a.id, a.name, a.points
FROM (
SELECT #rank:=#rank+1 AS iterator, id, name, points, auth
FROM table, (SELECT #rank:=0) tmp
ORDER BY points DESC) a
WHERE a.auth = 1
This should give you the record with rank for the record with auth = 1:
SELECT * FROM
(
SELECT id, auth, name, points, #rownum := #rownum + 1 AS rank
FROM (
SELECT id, auth, name, points
FROM yourTable
ORDER BY points DESC
) a
JOIN (
SELECT #rownum := 0
) r
) b
WHERE b.auth = 1;
sqlfiddle demo

MYSQL retrieving user activity where the same user_id can appear a maximum of 3 times

I'm retrieving rows from an user activity table like so
SELECT user_id, type, source_id FROM activity ORDER BY date DESC LIMIT 5
But I don't want the activity feed to be able to be clogged up by the same user, so I want to be able to retrieve a maximum of 3 rows out of 5 that contain the same user_id.
Any ideas how I could do this? Thanks :)
Here is a "traditional" way, where you first enumerate the user idsand use this information as a filter:
SELECT user_id, type, source_id
FROM (select a.*,
#rn := if (#user_id = user_id, #rn + 1, 1) as rn,
#user_id := user_id
from activity a cross join
(select #rn := 0, #user_id := -1) const
order by user_id
) a
WHERE rn <= 3
ORDER BY date DESC
LIMIT 5;
You can try this:-
SELECT user_id, type, source_id
FROM activity
WHERE 3 > (
SELECT count( * )
FROM activity AS activity1
WHERE activity .user_id = activity1.user_id
AND activity.user_id > activity1.user_id)
ORDER BY activity.user_id DESC
LIMIT 5