Im having issues performing a select on a table, but using the results of another table to filter the possibilities.
I have a table of player results (player id, wins, losses, draws, points), and rounds for the tournament are in another table (round id, tournament id, player 1 id, player 2 id and table id).
I've been trying to get a select statement that checks the tournamentround table and prevents the player from playing previous opponents (like a swiss tournament), but I run into the fact the player it choses for the second may then be chosen for another player.
ie p1 has played p2 and p3, but not p4 or p5, when the select happens it assigns p1 to p4, but when it gets to p4 it assigns p4 to p2.
Any idea on how to prevent this?
Example select:
SELECT * FROM playerresults WHERE ((SELECT pid FROM playerresults) != (SELECT p1id FROM tournamentrounds))
Any suggestions would be greatly appreciated
If you have a given player, say #playerid, then you can get players that have not been played by doing something like:
select pr.*
from playerresults pr
where pr.pid not in (select t.pid2 from tournaments t where pid1 = #playerid) and
pr.pid not in (select t.pid1 from tournaments t where pid2 = #playerid)
If you want a random one and your data is not very large (less than a few thousand rows), just add:
order by rand()
limit 1
to the query.
Related
I've got a database with 120k players. Each entry contains id, score (and more).
The goal is to get a highscore-list of not the top players, but instead of the N players above and and below a player, given his ID.
I currently try to solve this using two queries.
Query 1:
SELECT (
SELECT COUNT(*)
FROM players p2
WHERE p2.score > p1.score
) AS rank
FROM players p1
WHERE id = ID
returns the rank RANK of the player with an offset of -1. (for the best player it'll return 0)
Query 2:
SELECT id, score
FROM players
ORDER BY score DESC
LIMIT X OFFSET RANK;
returns a list with X=2*N+1 entries. I shift the $rank by -n to have the player that is doing the request in the middle (n players higher, current player, n players below).
So far, so good.
The actual issue now is, that for some scores there are more players with this score than X is big, which sometimes results in the player that should be in the middle of the list not even being contained in the X entries, but in some entries above or below.
To me it seems like a consistency problem, that query 1 returns a rank Y for player Z, but query 2 doesn't have player Z at it's Y'th position.
Can these queries be merged, or is there any other nice solution to this?
If the above stated is not clear, here's a minimalistic example:
n=1, requesting player called: C
database: A:123, B:123, C:123, D:123
Query 1 returns rank 3 for player C
Query 2 returns A:123, B:123, D:123 (being ranks 2-4)
C:123 should be in the middle, but the sorting of query 2 had C as rank 1.
The order of the elements with the same score in query 2 seems randomly
You can get the rank (position) in the highscore-list with something similar to the following query:
select * from (
select #rank:=#rank+1 rank, p.id
from players p
order by score desc
) t, (select #rank:= 0) t2
where id = :UID
After this query you can change the outer select to only get rank in the range of "rank" +- N
I have a high scoring (top scores) system, which is calculating positions by players's eperience.
But now I need to use the player's rank in other places just the web, maybe more places in the web too like personal
high scores, and it will show the player's rank in that skill.
Therefore just looping & playing with the loop cycle like rank++ won't really work, cause I need to save that rank for
other places.
What I could do is loop through all players and then send a query to update that player's rank, but what if i have 1000 players? or more?
that means 1000 queries per load.
I have thought if there could be a SQL query I can use to do the same action, in one or two queries.
How can I do this? I calculate ranks by ordering by player's eperience, so my table structure looks like this:
Tables:
Players
id (auto_increment) integer(255)
displayname varchar(255) unique
rank integer(255) default null
experience bigint(255)
This should give you the rank for user with id = 1. If you want every player, just remove the WHERE clause:
SELECT a.id, a.displayname, a.rank, a.experience
FROM (
SELECT id, displayname, #r:=#r+1 AS rank, experience
FROM players, (SELECT #rank:=0) tmp
ORDER BY experience DESC) a
WHERE a.id = 1
I wouldn't have rank in the players table directly, since this would mean that you would have to recalculate it every time a user changes experience. You could do this query anytime you want to get the rank for a player or for a leaderboard.
If you still want to update it, You can do an INNER JOIN with this query to UPDATE the original table with the rank from this query.
in our project we've got an user table where userdata with name and different kind of scores (overall score, quest score etc. is stored). How the values are calculated doesn't matter, but take them as seperated.
Lets look table 'users' like below
id name score_overall score_trade score_quest
1 one 40000 10000 20000
2 two 20000 15000 0
3 three 30000 1000 50000
4 four 80000 60000 3000
For showing the scores there are then a dummy table and one table for each kind of score where the username is stored together with the point score and a rank. All the tables look the same but have different names.
id name score rank
They are seperated to allow the users to search and filter the tables. Lets say there is one row with the player "playerX" who has rank 60. So if I filter the score for "playerX" I only see this row, but with rank 60. That means the rank are "hard stored" and not only displayed dynamically via a rownumber or something like that.
The different score tables are filled via a cronjob (and under the use of a addional dummy table) which does the following:
copies the userdata to a dummy table
alters the dummy table by order by score
copies the dummy table to the specific score table so the AI primary key (rank) is automatically filled with the right values, representing the rank for each user.
That means: Wheren there are five specific scores there are also five score tables and the dummy table, making a total of 6.
How to optimize?
What I would like to do is to optimize the whole thing and to drop duplicate tables (and to avoid the dummy table if possible) to store all the score data in one table which has the following cols:
userid, overall_score, overall_rank, trade_score, trade_rank, quest_score, quest_rank
My question is now how I could do this the best way and is there another way as the one shown above (with all the different tables)? MYSQL-Statements and/or php-code is welcome.
Some time ago I tried using row numbers but this doesn't work a) because they can't be used in insert statements and b) because when filtering every player (like 'playerX' in the example above) would be on rank 1 as it's the only row returning.
Well, you can try creating a table with the following configuration:
id | name | score_overall | score_trade | score_quest | overall_rank | trade_rank | quest_rank
If you do that, you can use the following query to populate the table:
SET #overall_rank:=-(SELECT COUNT(id) FROM users);
SET #trade_rank:=#overall_rank;
SET #quest_rank:=#overall_rank;
SELECT *
FROM users u
INNER JOIN (SELECT id,
#overall_rank:=#overall_rank+1 AS overall_rank
FROM users
ORDER BY score_overall DESC) ovr
ON u.id = ovr.id
INNER JOIN (SELECT id,
#trade_rank:=#trade_rank+1 AS trade_rank
FROM users
ORDER BY score_trade DESC) tr
ON u.id = tr.id
INNER JOIN (SELECT id,
#quest_rank:=#quest_rank+1 AS quest_rank
FROM users
ORDER BY score_quest DESC) qr
ON u.id = qr.id
ORDER BY u.id ASC
I've prepared an SQL-fiddle for you.
Although I think performance will weigh in if you start getting a lot of records.
A bit of explanation: the #*_rank things are SQL variables. They get increased with 1 on every new row.
I have a MySQL database where I add soccer games shown on TV. Each team is represented as an number. I can't really figure out how I can make a query to list how many times a team has been shown on TV, no matter if they played at home or away.
I'm trying to make a top 20 list of teams thats been shown on TV. The two columns I have team id in are called "hjemmehold" and "udehold" (it's danish :)).
Anyone can help me here?
SELECT Team, Count(*)
FROM (select Away as Team from Games union all select Home as Team from Games) t
GROUP BY Team
ORDER BY Count(*) Desc
LIMIT 20
SELECT SUM(Away)+SUM(Home) AS NumGames
FROM Games
WHERE Team=#Team
Obviously this is pseudocode, but put the proper tables/fields/params in and you should be good to go.
For all 20 teams:
SELECT TOP 20 Team, SUM(Away)+SUM(Home) AS NumGames
FROM Games
ORDER BY SUM(Away)+SUM(Home) desc
t.his should be able to give you the top 20 teams that have played. This will also allow you to find which teams have not yet played a game (if you remove the top 20 part).
SELECT TOP 20
team_id,
(SELECT COUNT(1) FROM GAMES WHERE HOME = t.team_id) + (SELECT COUNT(1) FROM GAMES WHERE AWAY = team_id) AS team_count
FROM TEAMS T
ORDER BY team_count DESC
I know I can do joins but its not exactly what I want.
I'm making a live chat system that has 2 tables mainly: the main chat table (call it table a), and then a mod table (call this one table b). If a user gets suspended, messages reach over 100 for that channel, or they are over 1 week, the messages get moved from the main chat table to the mod table.
I store the ID of the chat messages as ID(primary) on the main chat table and as chatID on the mod table.
What I'm doing is making a separate page for my Mods and I want to be able to combine the two tables into 1 area but I want them to be ordered by their respective tables.
So lets say we had the following:
Main table ID's: 1,2,4
Mod table ID: 3
I want my results to show up 1,2,3,4 no matter which table the ID is in.
Any help is greatly appreciated!
Edit: I got the answer and this is what I used to do so:
SELECT ab.* FROM
((SELECT ID as table_id FROM a
WHERE roomID = 'newUsers' ORDER BY ID ASC)
UNION ALL
(SELECT chatID as table_id FROM b
WHERE roomID = 'newUsers' ORDER BY chatID ASC)) ab
ORDER BY ab.table_id
Use a UNION in a subselect.
SELECT ab.* FROM (
SELECT 1 as table_id, * FROM a
UNION ALL
SELECT 2 as table_id, * FROM b
) ab
ORDER BY ab.id
If you want the result of table A to appear before table B, change the query to:
SELECT ab.* FROM (
SELECT 1 as table_id, * FROM a
UNION ALL
SELECT 2 as table_id, * FROM b
) ab
ORDER BY ab.table_id, ab.id
Some background
UNION ALL will merge two tables resultsets into one resultset.
UNION will do the same but will eliminate duplicate rows.
This takes time and slows things down, so if you know there will be no duplicate records (or you don't care about dups) use UNION ALL.
See also: http://dev.mysql.com/doc/refman/5.5/en/union.html