Creating Soccerleagueranking with Codeigniter & Mysql - php

I've searched the site for similar posts but i found just one where the developer tried to do his calculations (win-lose-draws) with an enormous SQL query. I would like to do the calculations in my controller but don't really know where to start.
I have 2 tables which look like this:
Teams
teamID teamName
Games
gameID matchday homeTeamID awayTeamID homeScore awayScore
Now i'm trying to produce a league ranking out of this match results, But i need some insights on how to look at this...
At the moment, I have a query which selects all the match results and assigns the correct teamID's to the home or away Team like this:
"SELECT g.gameID, g.matchday, g.homeTeamID, g.awayTeamID, g.homeScore, g.awayScore, th.teamName as homeTeam, ta.teamName as awayTeam,
FROM games AS g
INNER JOIN teams as th ON g.homeTeamID = th.teamID
INNER JOIN teams as ta ON g.awayTeamID = ta.teamID
JOIN submenu_teams AS s ON g.submenuID = s.submenuID"
Can anybody try to explain where to go from here to get a nice ranking of the teams according to how many points they won during the season?
Thnx!

I would suggest to keep track of the points in a table (season1) so that every time a page is requested, you don't have to compute the rankings again : you just fetch from the table.
Everytime a new match is played, run a script that adds X point to winner and substracts Y points from loser.
To display, fetch the results and order by score.
You're done !
(was it my post that you read on rankings and SQL ?)

Related

Optimization of query that uses three tables to order a list

So I have a query that works exactly as intended but needs heavy optimization. I am using software to track load times across my site and 97.8% of all load time on my site is a result of this one function. So to explain my database a little bit before getting to the query.
First I have a films table, a competitions table and a votes table. Films can be in many competitions and competitions have many films, since this is a many to many relationship we have a pivot table to show their relationship (filmCompetition) When loading the competition page however these films need to be in order of their votes (most voted at the top. least at the bottom).
Now In the query below you can see what I am doing, grabbing from the films from the filmsCompetition table that match the current competition id $competition->id, and then I order by the total number of votes for that film. Like I said this works but is super efficient but I cannot think of another way to do it.
$films = DB::select( DB::raw("SELECT f.*, COUNT(v.value) AS totalVotes
FROM filmCompetition AS fc
JOIN films AS f ON f.id = fc.filmId AND fc.competitionId = '$competition->id'
LEFT JOIN votes AS v ON f.id = v.filmId AND v.competitionId = '$competition->id'
GROUP BY f.id
ORDER BY totalVotes DESC
") );
For this query, you want indexes on filmCompetition(competitionId, filmId), films(id), and votes(filmId, competitionId)`.
However, it is probably more efficient to write the query like this:
SELECT f.*,
(SELECT COUNT(v.value)
FROM votes v
WHERE v.filmId = f.id and v.competitionId = '$competition->id'
) AS totalVotes
FROM films f
WHERE EXISTS (SELECT 1
FROM FilmCompetition fc
WHERE fc.FilmId = f.Filmid AND
fc.competitionId = '$competition->id'
)
ORDER BY TotalVotes DESC
This saves the outer aggregation, which should be a performance win. For this version, the indexes are FilmCompetition(FilmId, CompetitionId) and Votes(FilmId, CompetitionId).
The solution I actually ended up using was a little different but since Gordon answered the question I marked him as the correct answer.
My Solution
To actually fix this I did a slightly different approach, rather than trying to do all of this in SQL I did my three queries separately and then joined them together in PHP. While this can be slower in my case doing it my original way took about 15 seconds, doing it Gordons way took about 7, and doing it my new way took about 600ms.

Calculation of Points in a Database in 3rd NF

I have a database where the results from a shooter game are stored. I put them to 3NF to allow extensions of the system. So it looks like this:
Player
-------------------
GameId integer
PlayerId integer
TeamId integer
Hits
-------------------
GameId integer
FromId integer
ToId integer
Hits integer
So basically for every game there is a ID and every Player and Team has its ID (with their names stored in other databases)
Now I want to calculate points for each player. I need the points for each game but more importantly the total per player. The points are basically: 3 Points for each hit on opponent, -2 points for each hit of a team member and -2 points for each hit taken.
Alone the calculation of the number of team hits requires a JOIN with 3 tables and I fear for performance in production environment. (Each game has ~8 players-> PlayerDB-Size is 8n and HitsDB-Size is (8-1)^2*n)
And at the end: I need to calculate the points per player for each game and sum those up because the minimum points per game should be zero. And finally get a rank for each player (player x has the 2nd most total points etc)
I feel like I'm getting lost in overly complicated queries that will kill the database' performance at some point.
Could anyone judge the design and maybe give me some pointers where to start looking further? I though about storing the TeamHits and Points per Game in the players Database (Points for summing over them, teamHits for statistical purposes) but that would of course break normalization.
PS: I'm working with PHP 5 and MYSQL. I also thought about getting each game from the database, calculating the points in PHP (which I'm already doing when I show the game) and writing this back (optimally on putting in the game to the DB but also when the parameters for the points change)
Edit: Idea to avoid subselects would be:
SELECT p.*, SUM(h.Hits) AS TeamHits, SUM(h2.Hits) as Hits
FROM player p
LEFT JOIN
(hits h
INNER JOIN player p2
ON h.GameId=p2.GameId AND h.ToId=p2.PlayerId
)
ON p.GameId=p2.GameId AND h.FromId=p.PlayerId AND p.TeamId=p2.TeamId
GROUP BY p.PlayerId, p.GameId
LEFT JOIN hits h2
ON h2.GameId=p.GameId AND h2.FromId=p.PlayerId
But of course this does not work. Is it even possible to combine groupings with joins or will I have to use subqueries?
Best I have is:
SELECT p.PlayerId, SUM((-2-3)*IFNULL(th.TeamHits, 0) + (3)*IFNULL(h.Hits, 0) + (-2)*IFNULL(ht.HitsTaken, 0)) AS Points
FROM player p
LEFT JOIN
(SELECT p.GameId, p.PlayerId, SUM(h.Hits) AS TeamHits
FROM player p
INNER JOIN hits h
ON h.GameId=p.GameId AND p.PlayerId=h.FromId
INNER JOIN player p2
ON p.GameId=p2.GameId AND p2.PlayerId=h.ToId AND p.TeamId=p2.TeamId
GROUP BY p.PlayerId, p.GameId) th
ON p.GameId=th.GameId AND p.PlayerId=th.PlayerId
LEFT JOIN
(SELECT p.GameId, p.PlayerId, SUM(h.Hits) AS Hits
FROM player p
INNER JOIN hits h
ON h.GameId=p.GameId AND p.PlayerId=h.FromId
GROUP BY p.PlayerId, p.GameId) h
ON p.GameId=h.GameId AND p.PlayerId=h.PlayerId
LEFT JOIN
(SELECT p.GameId, p.PlayerId, SUM(h.Hits) AS HitsTaken
FROM player p
INNER JOIN hits h
ON h.GameId=p.GameId AND p.PlayerId=h.ToId
INNER JOIN player p2
ON p.GameId=p2.GameId AND p2.PlayerId=h.FromId AND p.TeamId!=p2.TeamId
GROUP BY p.PlayerId, p.GameId) ht
ON p.GameId=ht.GameId AND p.PlayerId=ht.PlayerId
GROUP BY p.PlayerId
Fiddle: http://sqlfiddle.com/#!9/dc0cb/4
Current problem: For a database with about 10,000 games calculating the points for all players takes about 18s. This is unusable, so I need to improve this...
Joins are not that expensive, subqueries are. as long as you can avoid subqueries you're not hitting too bad.
Remember, a database is built for this stuff these days.
Just make sure you have the proper indexes on the right fields so its optimised. Like teamID and GameID and playerID should be indexes.
Just run it in phpmyadmin and see how many milliseconds it takes to execute. if it takes more than 50 its a heavy query, but usually its pretty hard to hit this... I once managed to make a very heavy query that joined 100.000+ rows out of different tables and views and still did that in 5ms...
What numbers of requests a hour are we talking about? 200 players a day? 200.000 players a day? How often do the requests happen? 10 per second per player? once a minute? how loaded is your database?
I think that all these parameters are low, so you shouldnt worry about this optimisation yet.
Get your game up and running, clean up the php code where real gains can be had, and stay clear of complex subqueries or views.
As long as your table does joins and unions its pretty darn fast. and if you must do a subquery see if there is not an alternative way by using a linking table to link certain results to certain other tables so you can do a join instead of a subquery.

performing calculations on a database

I have a database setup with some sample data. I have a number of teams in one table and their fixtures in another in their fixtures I have their scores for the games they have played already. I am struggling with the logic of calculating their wins draws and losses. should I update this in the teams table when updating the results (in fixtures table) or calculate it from scores in the fixtures table. I was reluctant to do it the first way as it may be concidered duplicate data but can't figure out the logic of how to calculate it. as you can probably tell this is the first database I have worked on with relationships between tables.
I am trying to present data from the above tables in to a league table. in order to get points I need to calculate games won/d/lost and that is what I can't figure out how to do (count the number for times home team out socres away team etc)
I will remove most cols from teams if I can calculate it from fixtures table.
Datasets and calculations relating to various flavors of sportsball are surprisingly complex. I've written code to generate fixture schedules based on arena availability, and it's not fun.
Anyhow, in order to generate the report you're after without duplicating data all over the place something like the below should work, though I haven't been able to test it.
SELECT t.team_name,
hr.home_win, hr.home_loss, hr.home_draw,
ar.away_win, ar.away_loss, ar.away_draw
FROM teams t
-- home record
INNER JOIN (
SELECT home_team AS 'team_name',
SUM(IF(home_team_score>away_team_score,1,0)) AS 'home_win',
SUM(IF(home_team_score<away_team_score,1,0)) AS 'home_loss',
SUM(IF(home_team_score=away_team_score,1,0)) AS 'home_draw'
FROM fixtures
GROUP BY home_team
) hr ON t.team_name = hr.team_name
-- away record
INNER JOIN (
SELECT away_team AS 'team_name',
SUM(IF(home_team_score<away_team_score,1,0)) AS 'away_win',
SUM(IF(home_team_score>away_team_score,1,0)) AS 'away_loss',
SUM(IF(home_team_score=away_team_score,1,0)) AS 'away_draw'
FROM fixtures
GROUP BY away_team
) ar ON t.team_name = ar.team_name
Now, a normal RDBMS would just use COUNT(scoreA>scoreB), but since this is MySQL I had to fudge it with SUM(IF()). fiddle
Assuming that you're not going to have thousands of teams this should scale reasonably well.

Highscores (top scores) rank calculation & update query?

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.

This query (SELECT JOIN SUM ON) worked well before on another project however now it doesn't

The below query worked fine on another project (SQL), however now it seems to give no results and i do not get why. Have search allready for hours on the web for solutions.
I have 2 tables, one GOALS (id_goals, season, player, goalsscored, assists, team) and another PLAYERS (id_player, name, and some more fields not important for this matter). id_player from PLAYERS is used in player field in GOALS. Now i want to display the name of the players and the total goals they have scored as well as the total assists they have made in a certain season for a certain team and this is the query I am using (but does not work anymore):
mysql_select_db($database_check_mag, $check_mag);
$query_getPosts = "SELECT id, tot_goals, player_name, tot_assists FROM
(SELECT g.player id, SUM(g.goalsscored) tot_goals, SUM(g.assists) tot_assists,
g.season, s.name player_name, s.id_player FROM goals g JOIN players s ON
g.player = s.id_player WHERE g.season = 20132 AND g.team = 39
GROUP BY g.player) sums GROUP BY id ORDER BY tot_goals DESC";
Can somebody help me and tell me what I do wrong?

Categories