MySQL - Friends Leaderboard ranking by wins and include player in ranking - php

Platform: MySQL/PHP
Scenario: Create Friend Leaderboard which includes all friends of a player and also include the player in the leaderboard within a single query.
Details: I'm trying to create a query where the results include the player selected along with all their friends. Ranking will be determined on who has the greatest number of wins.
Question: Can this be done within a single query or is it faster to get the result set then compare each friend # of wins to the player # of wins to determine rank order.
Other Details:
Here is the query I have to return a player and their friends along with wins and money total:
SELECT a.profile_id, 'player', a.win AS player_wins, b1.profile_id AS friend_id,
b1.handle AS friend_handle, b1.win AS friend_wins, b1.bankroll AS friend_bank
FROM ldrbrd_cache_tmp a, buddy_tbl b, ldrbrd_cache_tmp b1
WHERE a.profile_id = b.profile_id
AND b.buddy_id = b1.profile_id
AND a.profile_id <> 1
AND b.buddy_id <> 1
AND a.profile_id = 2
ORDER BY a.profile_id, b1.win DESC;
Results:
id | name | player-wins | friend-id | friend-name | friend-wins | friend-money
'2','player' ,'200' ,'50' ,'Freddy K' ,'303' ,'10004572'
'2','player' ,'200' ,'53' ,'dannn' ,'217' ,'92065'
'2','player' ,'200' ,'51' ,'YoDood' ,'126' ,'2383600'
'2','player' ,'200' ,'58' ,'princess' ,'19' ,'89565'
'2','player' ,'200' ,'71' ,'com-on' ,'8' ,'-9530'
'2','player' ,'200' ,'57' ,'foolzzz' ,'7' ,'210'`
The results show the player-wins being 200 so in this case the player should be ranked #3.
You will notice I have the player wins but how can I compare the player wins along with the friend wins to return ranking within the same SQL statement? OR is the best course of action to just assign results to an array then determine where the player fits?
EDIT:
So I came up with a UNION that works, is this the only option??
SELECT profile_id, handle, win FROM (
SELECT a.profile_id, a.handle, a.win
FROM ldrbrd_cache_tmp a
WHERE a.profile_id = 2
UNION ALL SELECT b1.profile_id, b1.handle, b1.win
FROM ldrbrd_cache_tmp a, buddy_tbl b, ldrbrd_cache_tmp b1
WHERE a.profile_id = b.profile_id
AND b.buddy_id = b1.profile_id
AND a.profile_id <> 1
AND b.buddy_id <> 1
AND a.profile_id = 2
ORDER BY profile_id, win DESC) AS player_ranking
ORDER BY win DESC;
This seems to work for now, unless someone has a better solution?

Related

Filtering SQL Results with Tags in a Junction Table [duplicate]

Lets consider the following table-
ID Score
1 95
2 100
3 88
4 100
5 73
I am a total SQL noob but how do I return the Scores featuring both IDs 2 and 4?
So it should return 100 since its featured in both ID 2 and 4
This is an example of a "sets-within-sets" query. I recommend aggregation with the having clause, because it is the most flexible approach.
select score
from t
group by score
having sum(id = 2) > 0 and -- has id = 2
sum(id = 4) > 0 -- has id = 4
What this is doing is aggregating by score. Then the first part of the having clause (sum(id = 2)) is counting up how many "2"s there are per score. The second is counting up how many "4"s. Only scores that have at a "2" and "4" are returned.
SELECT score
FROM t
WHERE id in (2, 4)
HAVING COUNT(*) = 2 /* replace this with the number of IDs */
This selects the rows with ID 2 and 4. The HAVING clause then ensures that we found both rows; if either is missing, the count will be less than 2.
This assumes that id is a unique column.
select Score
from tbl a
where a.ID = 2 -- based off Score with ID = 2
--include Score only if it exists with ID 6 also
and exists (
select 1
from tbl b
where b.Score = a.Score and b.ID = 6
)
-- optional? ignore Score that exists with other ids as well
and not exists (
select 1
from tbl c
where c.Score = a.Score and c.ID not in (2, 6)
)

Calculate net change in ranking after "update", MYSQL

I have a two MYSQL tables:
Table-1
id catid title user_rating
123 8 title-one 3
321 8 title-two 5
and
Table-2
listing_id title user_rating
123 title-one 3
321 title-two 5
Plus, I have this query that calculates the current rank of each "title" based on "user_rating".
SELECT
MAX(x.rank) AS rank
FROM
(SELECT
a.id,
a.catid,
a.title,
b.listing_id,
#rank:=#rank + 1 AS rank
FROM
`table-1` a
INNER JOIN `table-2` b ON a.id = b.listing_id
CROSS JOIN (SELECT #rank:=0) r
WHERE
catid = '8'
ORDER BY user_rating DESC) x
WHERE
id = 123
Now, my issue: I want to calculate the difference in "ranking" (rank) when I update the "user_rating" value.
Please, note: the "user_rating" value is updated by a php script that allow users to vote for a specific content (range 1 to 5, step 0.5).
What's the best way to get the difference between the "previous rank" and "current rank" after the update?
Thanks in advance to all.

sql statement to alphabetize and count

Here is the mySQL I got
id terms
1 a
2 c
3 a
4 b
5 b
6 a
7 a
8 b
9 b
10 b
I want to get an alphabetized list sorted by count as follows
terms count
a 4
b 5
c 1
What mySQL statement do I need for that?
I believe something like this will work:
SELECT terms, COUNT( id) AS count
FROM table
GROUP BY terms
ORDER BY terms DESC
Read : GROUP BY (Transact-SQL)
Groups a selected set of rows into a set of summary rows by the values of one or more columns or expressions in SQL. One row is returned for each group. Aggregate functions in the SELECT clause list provide information about each group instead of individual rows.
You just need to apply group by clause for getting result
select terms, count (id) as count from table
group by terms
order by terms
I had a very similar need for a used record store to display artists in stock alphabetically with their count in parenthesis e.g.:
Smokey Robinson and The Miracles (2) | Sonic Youth (2) | Spoon (3) | Steely Dan (1) | Stevie Wonder (2) | Sufjan Stevens (1) |
Note that I used SELECT DISTINCT when pulling from my table "records". Here are the relevant code snippets:
//QUERY
$arttool = mysql_query("SELECT DISTINCT * FROM records GROUP BY artist ORDER BY artist ASC");
//OUTPUT LOOP START
while($row = mysql_fetch_array($arttool)){
//CAPTURE ARTIST IN CURRENT LOOP POSITION
$current=$row['Artist'];
//CAPTURING THE NUMBER OF ALBUMS IN STOCK BY CURRENT ARTIST
$artcount = mysql_num_rows(mysql_query("SELECT * FROM records WHERE artist = '$current'"));
//ECHO OUT.
echo $current . "($artcount)";
The actual code in my site is more complicated, but this is the bare bones of it. Hope that helps...

Counting rows in MySQL (highscore)

I've created a game with a highscore table in MySQL.
I have a "My scores" button that needs to retrieve the users scores, e.g.:
10. John 395
42. John 340
90. John 10
How should I go out retrieving the rank (10th, 42th, 90th) of each score of the user?
I could pull all the scores from the database and iterate through them but that doesn't seem like a good solution.
Let me try to expand:
I retrieve all MY scores from the database. E.g. 10 scores. I want to display these 10 scores however I won't know what the rank of these scores is compared to the other scores in my database! (10th, 16th, etc) ..Hope that makes more sense...
Thanks
For the position in the total list you either need to build up a list every time you want this overview, or use a stored procedure to build a list for a given moment. You could 'cache' a list on a given interval. Or maybe update a list when some one played a game that would change the top 100.
As #WhiteElephant suggested, you'd be making the table every time you want the data.
#stefandoorn suggest to not use the optimized count of sql, i think this is not efficient enough for these kind of computations.
A simple SQL query would do it for you. For example, if you want the 10th score, you could use:
SELECT name, score FROM highscores ORDER BY score DESC LIMIT 1 OFFSET 9
The offset will always be the position required - 1.
If you want to have the associated rank as a column beside the score, you could do the following:
SELECT #rownum:=#rownum+1 position, name, score FROM (SELECT #rownum:=0) r, highscores ORDER BY score DESC
This doesn't work with an offset (the position number will always start at 1). The result would be something like the following:
+----------+-------------+-------+
| position | name | score |
+----------+-------------+-------+
| 1 | Player 1 | 27681 |
| 2 | Player 2 | 14982 |
+----------+-------------+-------+
But I think the best solution is to just loop through the returned values with an index and use the index to keep track of the position.
SELECT
id, name, score,(select count(*) FROM highscores AS higherscores WHERE higherscores.score>currentscores.score)+1 AS rank
FROM
highscores AS currentscores
WHERE name="john"
;
Just use ORDER BYscoreDESC in the end of your query to sort them in reversed order (from high to low). When iterating and showing it in PHP you can use a count:
Query e.g.: SELECT * FROM scores ORDER BY score DESC
$count = 1;
$sql = 'SELECT * FROM scores ORDER BY score DESC';
$result = mysql_query($sql) or die(mysql_error());
while($fetch = mysql_fetch_object($result)) {
echo $count . ' ' . $fetch->score . '<br />';
$count++;
}

where id = multiple artists

Any time there is an update within my music community (song comment, artist update, new song added, yadda yadda yadda), a new row is inserted in my "updates" table. The row houses the artist id involved along with other information (what type of change, time and date, etc).
My users have a "favorite artists" section where they can do just that -- mark artists as their favorites. As such, I'd like to create a new feature that shows the user the changes made to their various favorite artists.
How should I be doing this efficiently?
SELECT *
FROM table_updates
WHERE artist_id = 1
OR artist_id = 500
OR artist_id = 60032
Keep in mind, a user could have 43,000 of our artists marked as a favorite.
Thoughts?
This depends on how your database is setup. If I had my way, I'd set it up with a table like so:
Table: user_favourite_artist
user_id | artist_id
---------------------
1 | 2
1 | 8
1 | 13
2 | 2
3 | 6
6 | 20
6 | 1
6 | 3
user_id and artist_id together would be a composite primary key. Each row specifies a user, by id, and an artist they have as a favourite, by id. A query like so:
SELECT artist_id FROM user_favourite_artist WHERE user_id = 1
Would give you the artist_id's 2, 8, and 13. This is a very simple query that will scale to your expectations.
On the reverse, when an artist is updated, you'd run this query:
SELECT user_id FROM user_favourite_artist WHERE artist_id = 2
And you would get the user_id's 1 and 2. This will tell you which users to notify. This query is also simple and will scale.
Maybe you can try this:
SELECT *
FROM table_updates
WHERE artist_id IN(1, 500, 60032)
If you have the marked artists in a secondary table, I would recomend rather using a join.
Something like
SELECT *
FORM table_updates tu INNER JOIN
table_marked_by_user tmbu ON tu.artist_id = tmbu.artist_id
WHERE tmbu.user_id = $user_id
If you're on SQL Server, you can use a nested select statement:
select * from table_updates where artist_id in
(select artist_id from favorites_table where user_id = 10)
If you don't mind doing dirty reads, you can speed it up with (nolock).
select * from table_updates (nolock) where artist_id in
(select artist_id from favorites_table (nolock) where user_id = 10)

Categories