My issue is I am trying to calculate all user entries and then rank them. After I rank them I want to update the users table to record each user's rank.
Tables:
userTbl
activityTbl
I have search all night and this is what I have so far.
SET #rank := 0;
SELECT * FROM (
SELECT #rank := #rank + 1 AS rank, a.userName
FROM activityTbl a
LEFT JOIN userTbl u ON a.userName = u.userName
GROUP BY a.userName
ORDER BY SUM( TIME_TO_SEC( a.actTime ) )DESC
) as sub
This returns:
rank|user
1 Kim
2 John
3 Joe
This is fine, but I've tried to add Update and Set to this query to update the userTbl and I just can't get it to work.
Please help! Let me know if you need more information.
UPDATE:
For those looking for exactly what I did to correct my problem.
First, I posted the wrong MySQL. The one I used to fix my problem is:
SET #rownum := 0;
SELECT #rownum := #rownum + 1 AS rank, userName, actTimeTotal
FROM (SELECT SUM(TIME_TO_SEC(actTime)) AS actTimeTotal , userName
FROM activityTbl
GROUP BY userName
ORDER BY actTimeTotal DESC) as result
Second, I created a rank table called rankTbl with 2 columns: rank smallInt and userName varchar.
Third, modified MySql script to:
SET #rownum := 0;
INSERT INTO rankTbl( rank, userName)
SELECT #rownum := #rownum + 1 AS rank, userName
FROM (SELECT SUM(TIME_TO_SEC(actTime))AS actTimeTotal , userName
FROM activityTbl
GROUP BY userName
ORDER BY actTimeTotal DESC) as result
ON DUPLICATE KEY UPDATE userName = VALUES(userName);
I changed the ON DUPLICATE KEY UPDATE to userName instead of rank. When rank changed the rank did not update for the userName. Using userName update changes in rank.
What you need to do is make sure that your ranking table has primary key on the user and then issue an INSERT of the SELECT with a KEY error provision:
Asuming your table is named tbl and contains a rank field and a user field, what I'm basically saying is that you need the following query to return a duplicate key error for all rows:
SET #rank := 0;
INSERT INTO tbl (rank, user)
SELECT #rank := #rank + 1 AS rank, a.userName
FROM activityTbl a
LEFT JOIN userTbl u ON a.userName = u.userName
GROUP BY a.userName
ORDER BY SUM(TIME_TO_SEC(a.actTime)) DESC;
Once you have that and you are sure it returns a duplicate key error for all rows it is trying to insert, you modify it as follows:
SET #rank := 0;
INSERT INTO tbl (rank, user)
SELECT #rank := #rank + 1 AS rank, a.userName
FROM activityTbl a
LEFT JOIN userTbl u ON a.userName = u.userName
GROUP BY a.userName
ORDER BY SUM(TIME_TO_SEC(a.actTime)) DESC
ON DUPLICATE KEY UPDATE rank = VALUES(rank);
Related
I'm trying to get the position of a specific user in a leaderboard for basketballScore. There is only one table in the database.
Here is the image of the database in mySQL
I imagine it would be something like this:
SELECT userID, SUM(points) as '`basketballScore`'
FROM user
ORDER BY SUM(points) DESC
You need a group by clause:
select userID,
SUM(score) as basketballScore
from user
group by userID
order by basketballScore desc
Also note that I reused the alias basketballScore in the order by clause.
If you need to create ranks, you can use user variables:
set #rank := 0;
select #rank := #rank + 1 as rank,
userID,
SUM(score) as basketballScore
from user
group by userID
order by basketballScore desc
You can get the rank of a specific user by doing:
select 1 + count(*) as rank
from user u
where u.basketballScore > (select u2.basketballScore from user u2 where u2.userId = #userId);
To bypass a problem I posted in a other thread. I tried an sql statement like this:
UPDATE user u JOIN (SELECT #i := 0) r
SET user_rank_planets = (#i := (#i + 1))
WHERE user_active=1
ORDER BY user_planets DESC
I got Error #1221. Without the order by clause, the statement works fine.
Is there someone who knows a solution for this issue?
You cannot use order by and limit in update statement in the case of multiple tables.
Quoting From MySQL Documentation:
For the multiple-table syntax, UPDATE updates rows in each table named
in table_references that satisfy the conditions. Each matching row is
updated once, even if it matches the conditions multiple times. For
multiple-table syntax, ORDER BY and LIMIT cannot be used.
UPDATE user u
INNER JOIN
(
SELECT
*,
(#i := (#i + 1)) AS row_number
FROM user u
CROSS JOIN (SELECT #i := 0) r
WHERE user_active=1
ORDER BY user_planets DESC
)AS t
ON u.Primary_key = t.primary_key
SET u.user_rank_planets = t.row_number.
Note: Replace u.Primary_key and t.primary_key by the primary key of user table.
Read first few paragraphs http://dev.mysql.com/doc/refman/5.7/en/update.html
The #1000111's answer doesn't work. I don't know the reason but MySQL just ignored the ORDER BY in the subquery and updated with the default order (by the Primary_key).
A silly solution is wrapping the subquery inside another subquery to create a temporary table.
UPDATE user u
INNER JOIN
(
SELECT * FROM (
SELECT *, (#i := (#i + 1)) AS row_number
FROM user u
CROSS JOIN (SELECT #i := 0) r
WHERE user_active=1
ORDER BY user_planets DESC
) AS t1
) AS t
ON u.<Primary_key> = t.<Primary_key>
SET u.user_rank_planets = t.row_number
Table A shows results that I have by running the following SQL in MySQL.
SELECT * FROM table
WHERE MATCH (title) AGAINST ('marka tv')
Table A
Table B shows results that I want to get. As you can see the groups are in round-robin order.
Table B
If I understand the question, you want to sort the output so the groups are in a round-robin fashion rather than ordered. You can do this by enumerating the values within each group and then using that information for sorting:
SELECT t.*
FROM (SELECT t.*,
(#rn := if(#g = groups, #rn + 1,
if(#g := groups, 1, 1)
)
) as rn
FROM table t CROSS JOIN
(SELECT #rn := 0, #g := '') params
WHERE MATCH (title) AGAINST ('marka tv')
ORDER BY groups
) t
ORDER BY rn, groups;
Consider a subquery in a derived table to calculate a group number to be sorted at final table:
SELECT f.*
FROM
(SELECT t1.* ,
(SELECT count(*)
FROM table t2
WHERE (t2.title <= t1.title)
AND (t1.groups = t2.groups)) AS groupNo
FROM table t1
WHERE MATCH (t1.title) AGAINST ('marka tv')
) AS f
ORDER BY groupNo, groups
The below query is working absolutely fine, as I need. All the user get unique RANKS (User of same points should not get same rank)
SELECT
id,
first_name,
email,
(SELECT
rank
FROM ( SELECT
#rownum:=#rownum+1 rank,
u.id AS user_id,
points
FROM
user_master u, (SELECT #rownum:=0) r
ORDER BY
points
DESC) AS tmp
WHERE
user_id = um.id) AS Rank,
registered_date AS registered,
um.points as Points
FROM
user_master um
ORDER BY
um.id ASC
Now I want to make view for this query, it gives me error message
View's SELECT contains a subquery in the FROM clause
I've also tried first to make a view of user ranks to merge 2 different views. The below query gives perfect rankings of user but when I try to make view of this:
SELECT
#rownum:=#rownum+1 rank,
id AS user_id,
points
FROM
user_master u, (SELECT #rownum:=0) r
ORDER BY
points
DESC
..it gives me error message:
View's SELECT contains a variable or parameter
Is there any other way to apply rank in this query (Rank must be unique even if points are same).
Give this a go:
create view test_view as SELECT t.id,t.first_name,t.email,
(select sum(case when t1.points > t.points then 1
when t1.points = t.points and t1.id < t.id then 1
else 0 end) from user_master t1)+1 as rank, t.registered_date AS registered,
t.points as Points
from user_master t
order by points desc;
Say I have a table like ID, NAME, SCORE. Now normally, to get the rankings of the teams, I'd select all and order by. Sometimes though, I don't want to know all the rankings, just the ranking of one team. If I added a column RANK, is there any way for MySQL to automatically fill in those values for me based off of SCORE? (I believe MS Excel has this capability)
and if so, how does it handle ties?
thanks
You can calculate the rankings when you make your query:
SELECT * FROM (
SELECT teams.*, #rownum := #rownum + 1 AS rank
FROM teams, (SELECT #rownum := 0) T1
ORDER BY score DESC) T2
WHERE id = 1
It works by initializing a variable called rownum to 0 and then iterating over the rows in order of decreasing score. For each team the rownum is increased and the team is assigned a rank based on the current value of rownum. The outer select applies a where clause so that only one row is returned.
Here is an improved version that assigns the same rank to teams that have tied scores:
SELECT id, name, teams.score, rank FROM (
SELECT score, #rownum := #rownum + 1 AS rank
FROM (SELECT DISTINCT(score) FROM teams) T1, (SELECT #rownum := 0) T2
ORDER BY score DESC) T3
JOIN teams ON T3.score = teams.score
If this isn't fast enough for you, then use a trigger instead.
looks like I'm looking for a MySQL trigger
Get All Teams:
SELECT
s1.name,
s1.score,
COUNT(s2.name) + 1 AS rank
FROM scores s1
LEFT OUTER JOIN scores AS s2 ON (s1.score < s2.score)
GROUP BY s1.name
ORDER BY COUNT(s2.name)
Get One Team ('The Canucks'):
SELECT
s1.name,
s1.score,
COUNT(s2.name) + 1 AS rank
FROM scores s1
LEFT OUTER JOIN scores AS s2 ON (s1.score < s2.score)
GROUP BY s1.name
HAVING s1.name = 'The Canucks'
ORDER BY COUNT(s2.name)
The method shown in the above examples get the ranking dynamically (not filling a regular or temp table).
Note: both of these assume a given team only exists once in scores table for the rank value to be correct.