Selecting the next row in a mysql subselect - mysql

I've got a table of data thats ordered by a non-primary key e.g.
id | likes
4 | 6
2 | 5
5 | 2
3 | 2
1 | 2
I need a query to find the row after id #5 which would be id #3.
I've tried using row numbers and written this but it seems really inefficient
select * from (
SELECT l.id,
l.likes,
#curRow := #curRow + 1 AS row_number
FROM sk_posters l
JOIN (SELECT #curRow := 0) r
WHERE active = 'yes'
order by likes desc, id desc)
as mycount where row_number =
(select row_number from (
SELECT l.id,
l.likes,
#curRow := #curRow + 1 AS row_number
FROM sk_posters l
JOIN (SELECT #curRow := 0) r
WHERE active = 'yes'
order by likes desc, id desc)
as mycount
where id=5)+1 limit 1
If there a better, more efficient way to do this?

You can use limit at the end of query like
SELECT * FROM YOUR_TABLE LIMIT 2,1

Related

MySQL Broken Rank

I have the following query which returns some event details, the number of votes and a rank.
SELECT e.guid,
e.name,
(SELECT COUNT(ev.event_vote_id)
FROM event_vote sv
WHERE ev.event_uid = s.guid) AS votes,
#curRank := #curRank + 1 AS rank
FROM event e, (SELECT #curRank := 0) r
ORDER BY votes DESC
It returns the correct details including votes but the rank is broken.
Actual Result
guid | name | votes | rank
def test2 2 2
abc test1 1 1
ghi test3 0 3
jkl test4 0 4
Expected Result
guid | name | votes | rank
def test2 2 1
abc test1 1 2
ghi test3 0 3
jkl test4 0 4
For some reason test1 has a higher rank than test2.
I assume I need to use a JOIN but i'm unsure on the syntax.
You have to calculate the votes first, then calculate the ranking.
SELECT T.*, #curRank := #curRank + 1 AS rank
FROM ( SELECT e.guid,
e.name,
(SELECT COUNT(ev.event_vote_id)
FROM event_vote sv
WHERE ev.event_uid = s.guid) AS votes
FROM event e
) as T
CROSS JOIN (SELECT #curRank := 0) r
ORDER BY votes DESC
You have wrong result because SELECT section occurs before ORDER section, so you already have a rank but not necessary match the order you get at the end.
Can read more about it here:
Order Of Execution of the SQL query

Convert Row to Column mySQL with ID (not pivoting)

I have a value store in a database like this:
ID | Date | Value
----------------------------------------------
1 | 11/20 | 1
1 | 11/21 | 2
2 | 11/20 | 10
2 | 11/21 | 20
However, I need it to be like this:
Date | Value ID 1 | Value ID 2
----------------------------------------------
11/20| 1 | 10
11/21| 2 | 20
So the new column can be plot in a trend (column 1 = date, column 2 = value#1, column 3 = value #2, column 4 = value#4, etc).
Here is the query for a single tag:
SELECT *
FROM (
SELECT ID, _date, ESYNC_TAGSHISTORY.Val, #curRow := #curRow + 1 AS row_number
FROM ESYNC_TAGSHISTORY
JOIN (SELECT #curRow:=0) i
INNER JOIN ESYNC_TAGS ON ESYNC_TAGSHISTORY.TAGID=ESYNC_TAGS.ID
WHERE ESYNC_TAGS.NAME='I_TT_21052' AND ESYNC_TAGS.STATIONID=1 AND (_date BETWEEN now()-INTERVAL 45 MINUTE AND now()) ) s
WHERE row_number mod 60 = 0;
And the results:
ID | Date | Value ID 1 | Row
----------------------------------------------
1 | 11/20| 1 | 1
1 | 11/21| 2 | 2
EDIT :
With some modification my query look like this
SELECT *
FROM (
SELECT ID, _date, ESYNC_TAGSHISTORY.Val, #curRow := #curRow + 1 AS row_number,
if (ESYNC_TAGS.NAME='I_TT_21052', ESYNC_TAGSHISTORY.Val, NULL) as 'I_TT_21052',
if (ESYNC_TAGS.NAME='I_TT_91214', ESYNC_TAGSHISTORY.Val, NULL) as 'I_TT_40011'
FROM ESYNC_TAGSHISTORY
JOIN (SELECT #curRow:=0) i
INNER JOIN ESYNC_TAGS ON ESYNC_TAGSHISTORY.TAGID=ESYNC_TAGS.ID
WHERE ESYNC_TAGS.STATIONID=1 AND (_date BETWEEN now()-INTERVAL 5 MINUTE AND now()) ) s
WHERE row_number mod 1 = 0
ORDER BY ID ,_date;
Result look like this
SQL RESULT
My Problem now is to move the data from the last column at the same place as the other (get the value line up with the date)
EDIT #2 : Finally for further reference query look like this :
SELECT _date, I_TT_21052, I_TT_40011, row_number
From(
SELECT max(_date) as _date, max(I_TT_21052) as I_TT_21052, max(I_TT_40011) as I_TT_40011, #curRow := #curRow + 1 AS row_number
FROM (
SELECT ID, _date, ESYNC_TAGSHISTORY.Val,
if (ESYNC_TAGS.NAME='I_TT_21052', ESYNC_TAGSHISTORY.Val, NULL) as 'I_TT_21052',
if (ESYNC_TAGS.NAME='I_TT_91214', ESYNC_TAGSHISTORY.Val, NULL) as 'I_TT_40011'
FROM ESYNC_TAGSHISTORY
JOIN (SELECT #curRow:=0) i
INNER JOIN ESYNC_TAGS ON ESYNC_TAGSHISTORY.TAGID=ESYNC_TAGS.ID
WHERE ESYNC_TAGS.STATIONID=1 AND (_date BETWEEN now()-INTERVAL 24 HOUR AND now()) ) s
GROUP BY _date)v
WHERE row_number mod 150 = 0;
select
case when id=1 then count(Id) else 0 end) as Value1,
case when id=2 then count(Id) else 0 end) as Value2
from ESYNC_TAGSHISTORY
This is not exact but try this kind of query you will get result

SQL filter rows without join

I'm always "irk" by unnecessary join. But in this case, I wonder if it's possible to not use join.
This is an example of the table I have:
id | team | score
1 | 1 | 300
2 | 1 | 257
3 | 2 | 127
4 | 2 | 533
5 | 3 | 459
This is what I want:
team | score | id
1 | 300 | 1
2 | 533 | 4
3 | 459 | 5
Doing a query looking like this:
(basically: who's the best player of each team)
SELECT team, MAX(score) AS score, id
FROM my_table
GROUP BY team
But I get something like that:
team | score | id
1 | 300 | 1
2 | 533 | 3
3 | 459 | 5
But it's not the third player that got 533 points, so the result have no consistency.
Is it possible to get truthworthy results without joining the table with itself? How to achieve that?
You can do it without joins by using subquery like this:
SELECT id, team, score
FROM table1 a
WHERE score = (SELECT MAX(score) FROM table1 b WHERE a.team = b.team);
However in big tables this can be very slow as you have to run the whole subquery for every row in your table.
However there's nothing wrong with using join to filter results like this:
SELECT id, team, score FROM table1 a
INNER JOIN (
SELECT MAX(score) score, team
FROM table1
GROUP BY team
) b ON a.score = b.score AND a.team = b.team
Although joining itself is quite expensive, this way you only have to run two actual queries regardless how many rows are in your tables. So in big tables this method can still be hundreds, if not thousands of times faster than the first method with subquery.
You can use variables:
SELECT id, team, score
FROM (
SELECT id, team, score,
#seq := IF(#t = team, #seq,
IF(#t := team, #seq + 1, #seq + 1)) AS seq,
#grp := IF(#t2 = team, #grp + 1,
IF(#t2 := team, 1, 1)) AS grp
FROM mytable
CROSS JOIN (SELECT #seq := 0, #t := 0, #grp := 0, #t2 := 0) AS vars
ORDER BY score DESC) AS t
WHERE seq <= 3 AND grp = 1
Variable #seq is incremented each time a new team is met as the records are being processed in descending score order. Variable #grp is used to enumerate records within each team partition. Records with #grp = 1 are the ones having the greatest score value within the team slice.
Demo here
Unfortantly , MySQL doesn't support window functions like ROW_NUMBER() which could have solved this easily.
There are several ways on doing that:
NOT EXISTS() :
SELECT * FROM YourTable t
WHERE NOT EXISTS(SELECT 1 FROM YourTable s
WHERE t.team = s.team AND s.score > t.score)
NOT IN() :
SELECT * FROM YourTable t
WHERE (t.team,t.score) IN(SELECT s.team,MAX(s.score)
FROM YourTable s
GROUP BY s.team)
A correlated query:
SELECT distinct t.id,t.team,
(SELECT s.score FROM YourTable s
WHERE s.team = t.team
ORDER BY s.score DESC
LIMIT 1)
FROM YourTable t
Or a join which I understand you already have.
EDIT : I take my words back, you can do it with a variable like #GiorgosBetsos solution.
You could do something like this:
SELECT team, score, id
FROM (SELECT *
,RANK() OVER
(PARTITION BY team ORDER BY score DESC) AS Rank
FROM my_table) ranked_result
WHERE Rank = 1;
Some info on Rank functionality: Clicketyclickclick

first N row of each id in MySQL

I have a table for my users scores like this:
id | kills
----------
2 | 1
1 | 1
1 | 5
1 | 3
2 | 4
2 | 5
3 | 5
I want to get the first 2 rows of each player which have more than 2 kills. So the result should look like this
id | kills
----------
1 | 5
1 | 3
2 | 4
2 | 5
3 | 5
I tried this but it doesn't work:
SELECT *
FROM user_stats us
WHERE
(
SELECT COUNT(*)
FROM user_stats f
WHERE f.id=us.id AND f.kills > 2
) <= 2;
I suspect that you just want the two largest values for users that have kills > 2. If so, use variables:
select us.*
from (select us.*,
(#rn := if(#i = id, #rn + 1,
if(#i := id, 1, 1)
)
) as seqnum
from user_stats us cross join
(select #rn := 0, #i := -1) params
where us.kills > 2
order by us.id, kills desc
) us
where seqnum <= 2;
select * from user_stats
where (id,kills) in (select id, max(kills) from user_stats where kills > 2 group by id
union
select id, min(kills) from user_stats where kills > 2 group by id)
Try this. I am coming from Oracle, where rownum is a count of rows selected. This should have the same effect.
select #rownum:=#rownum+1, us.*
from user_stats us , (select #rownum := 0) r
where id in (
select id from user_stats f
group by id
having count(*) > 2
)
and #rownum < 3;
based on response of vkp. Take min and max when id has more then 1 kill
select id, max(kills)
from user_stats
group by id
having count(kills) > 2
union
select id, min(kills)
from user_stats
group by id
having count(kills) > 2
order by id

How to get the rank of a row in mysql query

this my database structure
table : players
id | name | score
1 | Bob | 600
2 | Alex | 1400
3 | John | 800
4 | sara | 2000
I need to select john's row and count what is the john' rank OrderBy score
as you see john is 3rd (800) , sara is 1st (2000), Alex is 2nd (1400) in score ranks
Select #rownum := #rownum + 1 AS rank form players where id=3 OrderBy score
any idea ?
You can do it by a subquery and count the players who has score more than the score of a certian id
Select count(*) as rank
from players
where score > (select score from players where id=3)
But if you want to have other information beside the rank you can do it by
SELECT ranks . *
FROM (
SELECT #rownum := #rownum +1 ‘rank’, p.id, p.score
FROM players p, (SELECT #rownum :=0)r
ORDER BY score DESC
) ranks
WHERE id =3
select rank
from
(
Select id, name, #rownum := #rownum + 1 AS rank
from players
cross join (select #rownum := 0) r
Order By score desc
) tmp
where id = 3
Might be easier to do a self join, where the joined table score is greater (to get the rows with a higher score) and just do a count:-
SELECT COUNT(*)
FROM players a
INNER JOIN players b
ON a.score >= b.score
WHERE a.id = 3
Question is what to do with equal scores.