I have the following mysql statement to bring back top 10 user data plus the data for the signed-in user (if the signed-in user is in the top 10 there'll only be 10 results total, otherwise there's 11)...
(SELECT user_id, points, amountLikes, amountComments
FROM all_scores_table
ORDER BY points desc, amountLikes desc, amountComments desc
LIMIT 0,10)
UNION
(SELECT user_id, points, amountLikes, amountComments
FROM all_scores_table
WHERE user_id = $session_user_id)
ORDER BY points desc, amountLikes desc, amountComments desc
The all_scores_table has exactly 1 row per user. I'm now trying to add rank information to the second query (e.g. if there's 11 rows, I want to know whether the session_user_id is 11th or 468th in the list).
I've read a bunch of similar posts but couldn't find an answer that seemed to apply to the way I've written my initial query by unioning sorted results - e.g. I can't just add a simple count of rows with points greater than my user, because if they're even I want to reference amountLikes, amountComments to find the true order.
I could of course do this in PHP with a second query, but am hoping someone notices an elegant way to achieve this in mysql alone
I would recommend not trying to be clever and use one query. Instead, just make two queries, one which finds the top ten and the other which finds the logged in user along with his numerical rank. For the first query, you can use what you already have:
SELECT
user_id, points, amountLikes, amountComments
FROM all_scores_table
ORDER BY points DESC, amountLikes DESC, amountComments DESC
LIMIT 10
As for assigning ranks, you can easily handle this in your PHP code when you are iterating over the result set. Just use a variable counter starting at 1.
To get the rank of the currently logged in user, it will take more work, because he could be buried in the midst of hundreds (or more) of records. One option is to use session variables:
SET #row_number = 0;
SELECT
(#row_number:=#row_number + 1) AS rank,
user_id, points, amountLikes, amountComments
FROM all_scores_table
WHERE user_id = $session_user_id
ORDER BY points DESC, amountLikes DESC, amountComments DESC
This second query should return a single record (assuming each user appears only once), containing the metadata you need along with a numerical rank.
In your PHP logic, you can check if the rank be in the top ten, and, if so, not display this extra single record on the bottom of your table.
Related
I'm trying to create a mysql query that looks through my scoreboard for a given playerid and then finds their highest score and then adding a rank to that score.
I've come quite close to what I'm trying to achieve with this code:
SELECT PlayerUUID, `iGamescore` as score, FIND_IN_SET( iGamescore, (
SELECT GROUP_CONCAT( iGamescore
ORDER BY iGamescore ASC )
FROM crystm_gameScoreboard )
) AS rank
FROM crystm_gameScoreboard
WHERE PlayerUUID = '4c8984f3-651a-48bc-ad1a-879624380fab'
LIMIT 1
Returns:
But I do know that this player has played multiple times and is therefore multiple times in the scoreboard. As seen here:
So the issue here is that yes, it does find the player and gives the rank correctly.. However, since it exists multiple times, it saves the very first score instead of the highest score. How would I fix my query to correct for this or would you instead of creating a new score every time they create a highscore for themselves, just update their previous record in the scoreboard?
Thanks in advance
To get the highest score you need a GROUP BY:
SELECT
PlayerUUID,
MAX(`iGamescore`) as score
RANK() OVER (ORDER BY MAX(`iGamescore`) DESC) as Rang
FROM crystm_gameScoreboard
GROUP BY PlayerUUID
ORDER BY 3 ASC
The order by 3 ASC makes the list sorted by rank
This post solved it:
MySQL - Rank user amongst list of top high-scores
Had everything I was looking for. I dont know why I could not find this post but when searching, keywords are important haha.
Thanks for inputs tho.
I'm creating a message board. When a topic gets a reply, the entire topic (all rows that has the topicid) must be bumped to the top of the forum. Pinned topics should always be displayed first, then followed by the topic that has the most recent post date because they are bumped to the top when they get a reply.
This is a pic of the table that contains the posts.
DB Table before query
I need 1 query that will do the following:
group all the topicid together (lets call this "group");
within each group sort the rows by parentid ascending, but NULL always is sorted first;
groups that are "pinned" are displayed first
then groups that have the "latest" post displayed first
The query should give the following results
Results of the query
I think this query will do what you want.
SELECT *
FROM messages m
ORDER BY IF(pinned='yes','9999-12-31 23:59:59', (SELECT MAX(date) FROM messages m2 WHERE m2.topicid = m.topicid)) DESC,
topicid, IFNULL(parentid, 0)
The first part of the order by ensures that groups that are pinned are ordered first, followed by groups that have the most recent post. It does this by selecting the maximum possible date when the group is pinned, otherwise the latest date for posts in that group and sorts by that value descending. The second part then sorts those posts by topicid, and the final part sorts by parentid. To ensure that posts with a NULL parentid sort first, we use an IFNULL clause on parentid to set the sorting value to 0 when parentid is NULL.
I've created an SQLFiddle to demonstrate this.
Edit This updated query will also sort pinned topics by latest date instead of just by topicid. It does this by adding 1000 years to the date of pinned posts, thus ensuring that pinned posts sort ahead of non-pinned posts while retaining the ordering between pinned posts as well.
SELECT *
FROM messages m
ORDER BY (SELECT MAX(IF(pinned='yes', date + interval 1000 year, date))
FROM messages m2
WHERE m2.topicid = m.topicid) DESC,
topicid, ifnull(parentid, 0)
Here's an updated SQLFiddle to demonstrate.
I'm afraid I can only answer parts of your question, but I'm giving it a shot and posting it anyway in the hope that it will help you solve your issue.
First, I'd grab the pinned messages (lastest first in this query):
SELECT * FROM messages WHERE pinned = 1 ORDER BY date DESC
Then, you can grab - let's say - the 10 most recent topics:
SELECT MAX(date) AS latest, topicid FROM messages GROUP BY topicid ORDER BY latest DESC LIMIT 10
Now you should know the topicids from which you want to display messages. You could "chain together" the messages with a join:
SELECT * FROM messages AS m1 JOIN messages AS m2 ON m1.postid = m2.parentid WHERE m1.topicid IN <some stuff here> ORDER BY m1.topicid ASC, m1.postid DESC
Sorry for the incomplete answer. Any comments to help me fill the gaps are welcome ;)
SELECT *
FROM users
ORDER BY highscore DESC
LIMIT 5 OFFSET 5
and
SELECT *
FROM users
ORDER BY highscore DESC
LIMIT 5 OFFSET 10
returning the same result. And they are different than the records when I omit the LIMIT clause! I searched the community. There are similar questions, but they are of no help.
EDIT: Here are the table data-
Presumably, the issue is that you have ties for highscore. When you have ties, then MySQL orders the rows with the same value in an arbitrary and indeterminate way. Even two runs of the same query can result in different orderings.
Why? The reason is simple. There is no "natural" order for sorting keys with the same value. SQL tables represent unordered sets.
To make the sorting stable, include a unique id as the last key in the ORDER BY:
SELECT u.*
FROM users u
ORDER BY u.highscore DESC, u.userId
LIMIT 5 OFFSET 5;
Then, when you fetch the next 5 rows, they will be different.
I have the following table that stores messages between users:
I need to display a list of all last message for a users (from all users that have had contact with).
You will see to users being 1000000002 & 1000000172 for example. I need to show the last message between them which could be rows 1 to 4 - but would be 4 as last time.
I have tried the query below but its still isn't right:
SELECT sender_userid,receiver_userid,message,message_read,`datetime` FROM messages
WHERE (receiver_userid='1000000172' OR sender_userid='1000000172') AND friendship_status=1 AND receiver_history=1
GROUP BY receiver_userid
ORDER BY `datetime` ASC;
I find the order by doesn't get the most resent - could be because its after the Group By.
Also find it treats the sender_userid & receiver_userid as different rows in the Group By. I'm unsure how to get the most resent out of both.
thankyou so very much
Your GROUP BY should throw you errors, but MySQL just gives you garbage instead :)
You do not even need a group by, you simply want a list of all messages in which user X is involved and you want to sort them, so you have most of the work domn allready:
SELECT *
FROM messages
WHERE (receiver_userid='1000000172' OR sender_userid='1000000172')
AND friendship_status=1 AND receiver_history=1
ORDER BY `datetime` DESC;
Mind that you want to sort descending if you want the most recent ones first!
use DESC instead of ASC. DESC means large one is first.
I'm creating a list for best movies which are based on the users votes like imdb. I have done the list with this sql query:
SELECT data_id, COUNT(point), SUM(point)
FROM voting_table
WHERE data_type='1'
GROUP BY data_id
order by SUM(point)/COUNT(point)
DESC limit 100
This works well but i want also the number of votes(point) affect the order. I mean if a movie gets average of 8 with 10 votes and another movie gets average of 8 but with 5 votes. The one which has more votes should be listed higher than the other one. How can i do it? And is the query i wrote is efficent for server performance?
There is function AGV, I suggest you use that.
sort by avg, then by count or sum
...
ORDER BY AVG(point) DESC, COUNT(point) DESC
...
As of performance, there is not much you can do wihout complicating data structure.
It should be fine as it is unless your site si going to be as popular as imdb.
If your voting table grows past the point where speedup is needed then you need to start precalculating stuff (for real time updates using triggers that update score in movies table or some other intermediate table dedicated for that, other methods)
Just add the second order you want separated by comma .try this
SELECT data_id, COUNT(point), SUM(point)
FROM voting_table
WHERE data_type='1'
GROUP BY data_id
order by SUM(point)/COUNT(point) ,COUNT(point)
DESC limit 100
Try changing your order by clause to be:
order by SUM(point)/COUNT(point) desc, COUNT(point) desc
As it stands, your query appears to be efficient.