Search code examples
mysqlrelational-databaserelational

Adding a sub query to an already giant query


This question is kind of a follow up to this question I asked a month ago.

Here are my three tables.

Fighters

fighter_id |  name
-----------------------
1          | John
2          | Steve
3          | Bill
4          | Bobby

Events

event_id  |  event_name  |  event_date
-------------------------------------------
1          | MMA         |  01/01/2010
2          | Cool        |  02/20/2010
3          | Yeaa!       |  04/15/2010

Fights

fight_id  |  fighter_a  |  fighter_b  |  winner  |  method  |  event
-----------------------------------------------------------------------
1         | 1           |  2          |  1       | Sub      | 1
2         | 4           |  1          |  4       | KO       | 2
3         | 1           |  3          |  NULL    | Draw     | 3

So these are the current results (there's a bunch of other columns but they're not relevant to the question)

result  |  opponent  |  method  |  event  |  date        
----------------------------------------------------------
Draw    | Bill       |  Draw    |  Yeaa!  |  04/15/2010  
Loss    | Bobby      |  KO      |  Cool   |  02/20/2010 
Win     | Steve      |  Sub     |  MMA    |  01/01/2010

I want to have two extra columns. One for fighter's record and one for his opponents record, both based on the date of the event. So record until that date and including that date but not further than that. Basically <= to the current row's date.

Desired results

result  | record   |  opponent  | opp. record |  method  | event  |  date        
-----------------------------------------------------------------------------
Draw    | 1-1-1    | Bill       |  0-0-1      |  Draw    | Yeaa!  |  04/15/2010  
Loss    | 1-1      | Bobby      |  1-0        |  KO      | Cool   |  02/20/2010 
Win     | 1-0      | Steve      |  0-1        |  Sub     | MMA    |  01/01/2010

So here's the code to get the current results:

SELECT
    (
        CASE
            WHEN a.winner = a.f_a THEN "Win"
            WHEN a.winner = a.f_b THEN "Loss"
            WHEN a.winner IS NULL THEN a.method
        END
    ) AS result,
    b.name AS opponent,
    b.fighter_id AS opponent_id,
    b.fighting_out_of_country AS opponent_country,
    a.method AS method,
    a.method_type AS method_type,
    a.round AS round,
    a.time AS time,
    c.event_id AS event_id,
    c.event_name AS event,
    c.event_date AS date,
    c.event_city AS event_city,
    c.event_state AS event_state,
    c.event_country AS event_country
FROM
    (
        SELECT 
            IF(fighter_b = :fighter_id_0, fighter_b, fighter_a) AS f_a,
            IF(fighter_b = :fighter_id_1, fighter_a, fighter_b) AS f_b,
            winner,
            method,
            method_type,
            round,
            time,
            event
        FROM 
            fights
        WHERE
            :fighter_id_2 IN (fighter_a, fighter_b)
    ) a
INNER JOIN
    fighters b ON a.f_b = b.fighter_id
INNER JOIN
    events c ON a.event = c.event_id
ORDER BY
    c.event_date DESC

Here's the query I'm using to get just one fighter's record, but I'm not sure how to integrate it into this main query to fetch records of both fighter and his opponent. Of course I end up with 4 values so I can manipulate them with PHP and HTML/CSS later. (wins/losses/draws/no_contests)

SELECT 
SUM(if(winner = :fighter_id_3, 1, 0)) AS wins,
SUM(if(winner <> :fighter_id_4, 1, 0)) AS losses,
SUM(if(method = "Draw", 1, 0)) AS draws,
SUM(if(method = "No Contest", 1, 0)) AS no_contests

FROM fights
WHERE :fighter_id_5 IN (fighter_a, fighter_b) 

Solution

  • Try this:

    SELECT
        (
            CASE
                WHEN a.winner = a.f_a THEN "Win"
                WHEN a.winner = a.f_b THEN "Loss"
                WHEN a.winner IS NULL THEN a.method
            END
        ) AS result,
        SUM(a.f_a IN (d.fighter_a, d.fighter_b) AND d.winner <=> a.f_a) AS fighter_wincount,
        SUM(a.f_a IN (d.fighter_a, d.fighter_b) AND d.winner IS NOT NULL AND d.winner <> a.f_a) AS fighter_losscount,
        SUM(a.f_a IN (d.fighter_a, d.fighter_b) AND d.method = 'Draw') AS fighter_drawcount,
        SUM(a.f_a IN (d.fighter_a, d.fighter_b) AND d.method = 'No Contest') AS fighter_nocontestcount,
        b.name AS opponent,
        SUM(a.f_b IN (d.fighter_a, d.fighter_b) AND d.winner <=> a.f_b) AS opponent_wincount,
        SUM(a.f_b IN (d.fighter_a, d.fighter_b) AND d.winner IS NOT NULL AND d.winner <> a.f_b) AS opponent_losscount,
        SUM(a.f_b IN (d.fighter_a, d.fighter_b) AND d.method = 'Draw') AS opponent_drawcount,
        SUM(a.f_b IN (d.fighter_a, d.fighter_b) AND d.method = 'No Contest') AS opponent_nocontestcount,
        b.fighter_id AS opponent_id,
        b.fighting_out_of_country AS opponent_country,
        a.method AS method,
        a.method_type AS method_type,
        a.round AS round,
        a.time AS time,
        c.event_id AS event_id,
        c.event_name AS event,
        c.event_date AS date,
        c.event_city AS event_city,
        c.event_state AS event_state,
        c.event_country AS event_country
    FROM
        (
            SELECT 
                fight_id,
                IF(fighter_b = :fighter_id, fighter_b, fighter_a) AS f_a,
                IF(fighter_b = :fighter_id, fighter_a, fighter_b) AS f_b,
                winner,
                method,
                method_type,
                round,
                time,
                event
            FROM 
                fights
            WHERE
                :fighter_id IN (fighter_a, fighter_b)
        ) a
    INNER JOIN
        fighters b ON a.f_b = b.fighter_id
    INNER JOIN
        events c ON a.event = c.event_id
    LEFT JOIN
        (
            SELECT 
                a.fighter_a,
                a.fighter_b,
                a.winner,
                a.method,
                b.event_date
            FROM
                fights a
            INNER JOIN
                events b ON a.event = b.event_id
        ) d ON 
            (a.f_a IN (d.fighter_a, d.fighter_b) OR a.f_b IN (d.fighter_a, d.fighter_b)) AND
            d.event_date < c.event_date
    GROUP BY
        a.fight_id
    ORDER BY
        c.event_date DESC