Using a.id IN (b.a_id, b.b_id)
is never going to be performant, you won't be able to create an index to fit the query. You should normalise that aspect of your structure.
I propose the following changes to your schema:
- Add
fight_date_time
to yourfights
table - Move
fighter_a
andfighter_b
out to a participicants table
fight_id
,fighter_id
- Normalised - Two rows per fight
The following will create two records for every fight (One for each fighter), but will also list their opponent, and both their records going in to the fight.
SELECT
event.event_name,
event.event_date,
MAX(CASE WHEN participant.fighter_id = fighter.id THEN fighter.name END) AS fighter,
MAX(CASE WHEN participant.fighter_id = fighter.id THEN record.wins END) AS wins,
MAX(CASE WHEN participant.fighter_id = fighter.id THEN record.draws END) AS draws,
MAX(CASE WHEN participant.fighter_id = fighter.id THEN record.losses END) AS losses,
CASE WHEN fight.winner = participant.fighter_id THEN 'Win'
WHEN fight.winner IS NULL THEN 'Draw'
ELSE 'Loss' END AS result,
fight.method,
MAX(CASE WHEN participant.fighter_id <> fighter.id THEN fighter.name END) AS Opp,
MAX(CASE WHEN participant.fighter_id <> fighter.id THEN record.wins END) AS Opp_wins,
MAX(CASE WHEN participant.fighter_id <> fighter.id THEN record.draws END) AS Opp_draws,
MAX(CASE WHEN participant.fighter_id <> fighter.id THEN record.losses END) AS Opp_losses
FROM
event
INNER JOIN
fight
ON event.id = fight.event_id
INNER JOIN
participant
ON participant.fight_id = fight.id
INNER JOIN
(
SELECT
participant.fighter_id,
participant.fight_id,
SUM(CASE WHEN prevFight.winner = participant.fighter_id THEN 1 ELSE 0 END) AS wins,
SUM(CASE WHEN prevFight.winner IS NULL) AS draws,
SUM(CASE WHEN prevFight.winner <> participant.fighter_id THEN 1 ELSE 0 END) AS losses
FROM
participant
INNER JOIN
fight
ON participant.fight_id = fight.id
INNER JOIN
participant AS prevParticipant
ON prevParticipant.fighter_id = participant.fighter_id
INNER JOIN
fight AS prevFight
ON prevFight.id = prevParticipant.fight_id
AND prevFight.fight_date_time < fight.fight_date_time
GROUP BY
participant.fighter_id,
participant.fight_id
)
AS record
ON record.fight_id = participant.fight_id
INNER JOIN
fighter
ON fighter.id = record.fighter_id
GROUP BY
event.id,
fight.id,
participant.fighter_id
If you want to see the results for just one fighter, add:
- WHERE participant.fighter_id = :fighter_id
Even better, keep this kind of data up to date with triggers. That way you don't need to calculate it again and again and again.