Query stopped working (getting the user's position in the leaderboard):
SELECT
`rank`, `uid`, `battleWinScore`
FROM
(SELECT
#rank:=#rank+1 AS `rank`, `uid`, `battleWinScore`
FROM
`rating`, (SELECT #rank := 0) r
ORDER BY `battleWinScore` DESC
) t
WHERE uid = 572;
In the rating table, we need to get the user's position by field battleWinScore.
I am absolutely not good at mysql. Help =)
If you are running MySQL 8.0, just use row_number() (or rank(), if you want to consistently allow ties).
select *
from (
select uid, battleWinScore, rank() over(order by battleWinScore desc) rn
from rating
) r
where uid = 572
In earlier versions, I would recommend a correlated subquery rather than user variables:
select uid, battleWinScore
(select count(*) + 1 from rating r1 where r1.battleWinScore > r.battleWinScore) as rn
from rating r
where uid = 572
Related
I currently need to order data by highest value down, and then lowest value up, in between.
My Query is close, but doesn't quite order by largest down, though it is inserting the lowest in between:
DEMO Fiddle
select users.*
from users CROSS JOIN (select #even := 0, #odd := 0) param
order by
IF(score > 1, 2*(#odd := #odd + 1), 2*(#even := #even + 1) + 1),
score DESC;
Current Results
Email Score
----- --------
foo1#gmail.com 42
foo5#gmail.com 1
foo2#gmail.com 49
foo6#gmail.com 0
foo3#gmail.com 37
foo4#gmail.com 7
foo#gmail.com 22
Desired Results
Email Score
----- --------
foo2#gmail.com 49
foo6#gmail.com 0
foo1#gmail.com 42
foo5#gmail.com 1
foo3#gmail.com 37
foo4#gmail.com 7
foo#gmail.com 22
You can achieve using MySQL but avoid such complex SQL statements as the same can be achieved using programming language very easily.
SET #totalRows := (CASE WHEN (SELECT COUNT(*) FROM users) IS NULL THEN 0 ELSE (SELECT COUNT(*) FROM users) END);
PREPARE stmt1 FROM '(SELECT t.* FROM (SELECT ROW_NUMBER() OVER (ORDER BY score DESC) AS row_num, email, score FROM users UNION ALL SELECT ROW_NUMBER() OVER (ORDER BY score ASC) AS row_num, email, score FROM users) AS t ORDER BY t.row_num, t.score DESC LIMIT 0,?)';
EXECUTE stmt1 USING #totalRows;
Here is the explanation to achieve it:
Set variable #totalRows contains total rows in users table as that many rows will be displayed as a final result set.
Used ROW_NUMBER() function of MySQL to set ordering based on SOCRE field DESCENDING and ASCENDING for another result set.
Combined both the result set using UNION ALL statement of MySQL
Add the LIMIT keyword to make sure the final result must have a total number of rows that should not exceed #totalRows variable.
As in MySQL LIMIT statement we can't pass a dynamic value at a query level. I used the approach of prepare a statement.
You can ignore row_num column I used as final result set.
Hope my solution will help you.
For MySql 8.0+ you can use ROW_NUMBER() window function:
SELECT email, score
FROM (
SELECT *,
ROW_NUMBER() OVER (ORDER BY score DESC) rn1,
ROW_NUMBER() OVER (ORDER BY score ASC) rn2
FROM users
) t
ORDER BY LEAST(rn1, rn2), rn1;
For previous versions you can simulate ROW_NUMBER() with correlated subqueries (with the cost of poor performance for large datasets):
SELECT email, score
FROM (
SELECT u1.*,
(SELECT COUNT(*) FROM users u2 WHERE u2.score > u1.score) rn1,
(SELECT COUNT(*) FROM users u2 WHERE u2.score < u1.score) rn2
FROM users u1
) t
ORDER BY LEAST(rn1, rn2), rn1;
See the demo.
I'm trying to generate a random sample of half of a table (or some other percentage). The table is small enough that I can use the ORDER BY RAND() LIMIT x approach. I'd like the code to sample 50% of recipients as the table changes size over time. Below was my first attempt but you can't put a subquery in a LIMIT clause. Any ideas?
SELECT
recipient_id
FROM
recipient
ORDER BY RAND()
LIMIT (
/* Find out how many recipients are on half the list */
SELECT
COUNT(*) / 2
FROM
recipient
);
If you are running MysQL 8.0, you can use window functions:
select *
from (select t.*, ntile(2) over(order by random()) nt from mytable t) t
where nt = 1
In earlier versions, one approach uses user variables:
select t.*
from (
select t.*, #rn := #rn + 1 rn
from (select * from mytable order by random()) t
cross join (select #rn := 0) x
) t
inner join (select count(*) cnt from mytable) c on t.rn <= c.cnt / 2
I have an issue with a query,
this is my old question that was previously solved
mysql get ranking position grouped with joined table
The problem is that when two players have the same score, the query returns the same ranking position, like 1-1-2-3 ecc. How can I fix this?
In the player's table there are also player_tickets (that is the number of game played) and player_date that is a timestamp.
I thought to get my ranking on base of player_score first, player_tickets then, and finally player_date
This is my older query
SELECT *,
(SELECT 1 + Count(*)
FROM players p2
WHERE p2.`player_game` = p.`player_game`
AND p2.player_score > p.player_score
AND p2.player_status = 0) AS ranking
FROM players p
ORDER BY `player_game`,
player_score DESC
You can simply add more columns to the order by of the window function:
rank() over (
partition by player_game_id
order by player_score desc, player_tickets desc, player_date
) as rank
If you really want to avoid having the same rank twice, you can also use row_number(), which guarantees this - when there are ties, row_number() affects distinct numbers (whose order is hence undefined).
Just add the ranking criteria to your WHERE clause:
SELECT *,
(
SELECT 1 + COUNT(*)
FROM players p2
WHERE p2.player_game = p.player_game
AND
(
(p2.player_score > p.player_score) OR
(p2.player_score = p.player_score AND p2.player_tickets > p.player_tickets) OR
(p2.player_score = p.player_score AND p2.player_tickets = p.player_tickets AND p2.player_date > p.player_date) OR
(p2.player_score = p.player_score AND p2.player_tickets = p.player_tickets AND p2.player_date = p.player_date AND p2.player_id > p.player_id)
)
) AS ranking
FROM players p
ORDER BY player_game, player_score DESC;
You can add additional comparisons in the subquery. Or more easily, use variables:
select p.*,
#rn := if(#g = player_game, #rn + 1,
if(#g := player_game, 1, 1)
) as ranking
from (select p.*
from players p
order by player_game, player_score desc, player_tickets desc, player_date desc
) p cross join
(select #rn := 0, #g := 0) as seqnum;
In newer versions, you would just use row_number() if you don't want ties.
I was wondering if it's possible to use a subquery inside a LIMIT.
The reason why I'd like to use this, is to return 20% (1/5th) of the best buying customers.
For instance (though this clearly doesn't work):
SELECT id, revenue
FROM customers
ORDER BY revenue DESC
LIMIT (SELECT (COUNT(*) / 5) FROM customer)
Is there a way to make a subquery in a limit, or return 20% in a different way?
A typical way of doing this using ANSI SQL is with window functions:
SELECT id, revenue
FROM (SELECT c.*,
ROW_NUMBER() OVER (ORDER BY revenue DESC) as seqnum,
COUNT(*) OVER () as cnt
FROM customers
) c
WHERE seqnum <= cnt * 0.2
ORDER BY revenue DESC;
Most databases support these functions.
MySQL is one of the few databases that do not support window functions. You can use variables:
SELECT id, revenue
FROM (SELECT c.*, (#rn := #rn + 1) as rn
FROM customers c CROSS JOIN
(SELECT #rn := 0) params
ORDER BY c.revenue DESC
) c
WHERE rn <= #rn / 5; -- The subquery runs first so #rn should have the total count here.
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