MySQL: Getting a row number (ranking) for a specific row - mysql

I have a users table that has a column called money_sent. I want to order this table by money_sent in descending order, and then find out what "rank" a specific user has.
For example, only 111 people have spent more money than User 12392, so they would be rank 112.
How could I query this?

How about:
SELECT count(*) FROM users WHERE money_sent < (
SELECT money_sent FROM users WHERE user = 'joe'
);

SELECT Row,user, money_sent
FROM (SELECT #row := #row + 1 AS Row, user, money_sent
FROM table1 order by money_sent desc)
As derived1

If you also want to get the user's row along with that user's rank, you can use something like this:
SELECT u1.*, COUNT(u2.user)
FROM users u1
LEFT OUTER JOIN users as u2 ON (u1.money_sent < u2.money_sent)
GROUP BY u1.user;

Related

Order rows by amount of columns in another table

I'm currently outputting all of my members by adding the MySQL clause ORDER BY id DESC, but I feel that doesn't reward people that are active on my service.
I thought about judging the order by the amount of entries in another table they have under their ID.
Essentially, I'm asking if it's possible to order columns in a MAIN table counting the amount of rows where the users ID is in the column of the row.
Something pseudo to this
SELECT user_id,name,etc FROM users ORDER BY (
COUNT(SELECT FROM users_interactions WHERE user_id = user_id) *******
) ASC
In the end of the COUNT statement, the user_id = user_id was just a guess.
You are almost there - what you need to do is to put COUNT inside SELECT:
SELECT user_id,name,etc FROM users u ORDER BY (
SELECT COUNT(*)
FROM users_interactions i
WHERE i.user_id = u.user_id
) ASC
You could also do it using a JOIN, like this:
SELECT u.user_id, u.name, u.etc
FROM users u
LEFT OUTER JOIN users_interactions i ON i.user_id = u.user_id
GROUP BY u.user_id, u.name, u.etc
ORDER BY COUNT(*) ASC

MYSQL using other tables

This query:
SELECT
user_id,
count(base_item)
FROM items
WHERE base_item = '202'
group by user_id order by count(base_item)
Gives me this result:
which I want.
However, I also want it to exclude all user ids in the users table with a rank of 5 or greater. as shown here
Modify your where clause this way:
WHERE base_item = '202' AND user_id NOT IN (SELECT id FROM users WHERE rank > 5)
The portion in parentheses is called a subquery. The result set of the subquery contains the id of all users with a rank greater than 5. The addition to the where clause excludes all users in that result set.
Join with the users table and filter out rows with high rank
SELECT user_id, count(*) AS count
FROM items AS i
JOIN users AS u ON i.user_id = u.id
WHERE i.base_item = '202'
AND u.rank <= 5
group by user_id
order by count

Listing last (N) message of each conversation

I am creating a mobile application that need to synchronize with the server, and In order to do so, I need to get last (N) messages in each conversation.
note that this query was worked but get only last message in each conversation.
SELECT users.user_id AS user_id,
users.username,
users.picture,
users.last_seen,
me.message,
me.created_on
FROM messages me,
users
WHERE (me.sender_id=1
OR me.recipient_id=1)
AND ((me.sender_id=user_id
AND me.sender_id<>1)
OR (me.recipient_id=user_id
AND me.recipient_id<>1))
AND NOT exists
(SELECT 1
FROM messages me2
WHERE me2.id>me.id
AND ((me.sender_id=me2.sender_id
AND me.recipient_id=me2.recipient_id)
OR (me.sender_id=me2.recipient_id
AND me.recipient_id=me2.sender_id)))
ORDER BY me.created_on DESC
First we get all messages of all users ordered by user and date. We introduce artificial vars to number messages of user. When user id is the same we just increase message number. If it's different reset it to 0.
Thus subquery returns us
user_id, mess_n
1 0
1 1
1 2
2 0
2 1
2 2
3 0
3 1
3 2
Then in the query just leave messages with number <10 (first 10)
select *
from (
select u.*, m.*,
#mess_n_for_user:=if(u.user_id!=#curr_user,0,#mess_n_for_user+1) as mess_n,
#curr_user:=u.user_id
from (SELECT #mess_n_for_user:=0, #curr_user:=-1) sess_var,
users u join messages m on (u.user_id=m.sender_id
or u.user_id=m.recipient_id)
order by u.user_id, m.created_on DESC) all_messages_ordered
where all_messages_ordered.mess_n<10
Just add all filters conditions to the query
UPDATED
FROM the sqlfiddle
select * from (
select all_messages_ordered.*,
#mess_n_for_user:=if(u_id!=#curr_user,0,#mess_n_for_user+1) as mess_n,
#curr_user:=u_id
from (SELECT #mess_n_for_user:=0, #curr_user:=-1) sess_var,
(
select u.id as u_id, u.first_name, m.*
from
accounts u join messages m on (u.id=m.from or u.id=m.to)
and (m.to=1 or m.from=1)
and u.id<>1
order by u.id, m.date_time DESC) all_messages_ordered) a
where mess_n<3
You need a recursive query to select conversations. Following the approach from this answer, substituting recipient_id / sender_id for col3 / col1 :
select id, recipient_id, #pv:=sender_id as 'recipient_id' from messages
join
(select #pv:=2)tmp
where recipient_id=#pv
I have not tested this but the principle should be correct. Of course you will need to expand this to fully solve your problem...but hope this helps.

How to count rows on joined table

I have 2 tables -- a master (called groups) and details (called users)
I want to return each and every master row for subsequent display, but I only want to count how many detail rows match the master
My query is not working properly -- it only returns the first row in the master table
$query_string = '
SELECT groups.userGroupID, userGroup,
count(users.userGroupID) AS howMany
FROM groups_table AS groups
JOIN users_table AS users ON users.userGroupID = groups.userGroupID
ORDER BY groups.userGroupID
';
Thanks for helping.
Forgot your group by:
SELECT groups.userGroupID, userGroup,
count(users.userGroupID) AS howMany
FROM groups_table AS groups
LEFT JOIN users_table AS users ON users.userGroupID = groups.userGroupID
GROUP BY groups.userGroupID
ORDER BY groups.userGroupID
This works for me, hope it helps anyone else looking for same answer
SELECT COUNT(howMany) AS fo2
FROM(
SELECT groups.userGroupID, userGroup,
users.userGroupID AS howMany
FROM groups_table AS groups
JOIN users_table AS users ON users.userGroupID = groups.userGroupID
ORDER BY groups.userGroupID) AS myCount

querying for user's ranking in one-to-many tables

I am trying to write a query to find the score rank of a user's games. I need it to take in a user id and then return that user's relative ranking to other user's scores. There is a user and a game table. The game table has a userId field with a one-to-many relationship.
Sample table:
users:
id freebee
1 10
2 13
games:
userId score
1 15
1 20
2 10
1 15
passing $id 1 into this function should return the value 1, as user 1 currently has the highest score. Likewise, user 2 would return 2.
Currently this is what I have:
SELECT outerU.id, (
SELECT COUNT( * )
FROM users userI, games gameI
WHERE userI.id = gameI.userId
AND userO.id = gameO.userId
AND (
userI.freebee + SUM(gameI.score)
) >= ( userO.freebee + SUM(gameO.score) )
) AS rank
FROM users userO,
games gameO
WHERE id = $id
Which is giving me an "invalid use of group function" error. Any ideas?
SELECT u.id,total_score,
( SELECT COUNT(*) FROM
(SELECT u1.id, (IFNULL(u1.freebee,0)+ IFNULL(SUM(score),0)) as total_score
FROM users u1
LEFT JOIN games g ON (g.userId = u1.id)
GROUP BY u1.id
)x1
WHERE x1.total_score > x.total_score
)+1 as rank,
( SELECT COUNT(DISTINCT total_score) FROM
(SELECT u1.id, (IFNULL(u1.freebee,0)+ IFNULL(SUM(score),0)) as total_score
FROM users u1
LEFT JOIN games g ON (g.userId_Id = u1.id)
GROUP BY u1.id
)x1
WHERE x1.total_score > x.total_score
)+1 as dns_rank
FROM users u
LEFT JOIN
( SELECT u1.id, (IFNULL(u1.freebee,0)+ IFNULL(SUM(score),0)) as total_score
FROM users u1
LEFT JOIN games g ON (g.userId = u1.id)
GROUP BY u1.id
)x ON (x.id = u.id)
rank - (normal rank - e.g. - 1,2,2,4,5), dns_rank - dense rank (1,2,2,3,4). Column total_score - just for debugging...
The query does not like the reference of an outer table in the Sum function SUM(gameO.score) in the correlated subquery. Second, stop using the comma format for joins. Instead use the ANSI syntax of JOIN. For example, in your outer query did you really mean to use a cross join? That is how you wrote and how I represented it in the solution below but I doubt that is what you want.
EDIT
I've adjusted my query given your new information.
Select U.id, U.freebee, GameRanks.Score, GameRanks.Rank
From users As U
Join (
Select G.userid, G.score
, (
Select Count(*)
From Games As G2
Where G2.userid = G.userid
And G2.Score > G.Score
) + 1 As Rank
From Games As G
) As GameRanks
On GameRanks.userid = U.id
Where U.id =1
I'm not a MySQL person, but I believe that the usual way to do ranking in it is using a variable within your SQL statement. Something like the below (untested):
SELECT
SQ.user_id,
#rank:=#rank + 1 AS rank
FROM
(
SELECT
U.user_id,
U.freebee + SUM(COALESCE(G.score, 0)) AS total_score
FROM
Users U
LEFT OUTER JOIN Games G ON
G.user_id = U.user_id
) SQ
ORDER BY
SQ.total_score DESC
You could use that as a subquery to get the rank for a single user, although performance-wise that might not be the best route.
Here is "simplified" version for calculating a rank based only on "games" table. For calculating rank for a specific game only you need to add additional joins.
SELECT COUNT(*) + 1 AS rank
FROM (SELECT userid,
SUM(score) AS total
FROM games
GROUP BY userid
ORDER BY total DESC) AS gamescore
WHERE gamescore.total > (SELECT SUM(score)
FROM games
WHERE userid = 1)
It's based on the idea that ranking == number of players with bigger score + 1
Check this out:
http://rpbouman.blogspot.com/2009/09/mysql-another-ranking-trick.html