复杂查询运行时间太长

时间:2012-11-05 06:05:29

标签: mysql sql relational

我有一个完美的查询(original question that has table structure too),但是它会创建一个临时表,由于数据量的原因,这个表很可能需要12秒。 (1表有95k记录,另有155k,另有21k)。

有没有办法绕过临时表解决方案或让它运行得更快?也许建议应该索引哪些字段?我有ID字段,日期字段等...索引,但这根本没有帮助。

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_3, fighter_b, fighter_a) AS f_a,
            IF(fighter_b = :fighter_id_4, fighter_a, fighter_b) AS f_b,
            winner,
            method,
            method_type,
            round,
            time,
            event
        FROM 
            fights
        WHERE
            :fighter_id_5 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
    date DESC

1 个答案:

答案 0 :(得分:0)

使用a.id IN (b.a_id, b.b_id) 从不将具有高效性,您将无法创建适合查询的索引。您应该规范化结构的这一方面。

我建议您对架构进行以下更改:

  • fight_date_time添加到您的fights表格
  • fighter_afighter_b移至参与者表格
    • fight_idfighter_id - 标准化 - 每次战斗两行


以下将为每场战斗创建两个记录(每个战斗机一个),但也会列出他们的对手,以及他们的记录都参加战斗。

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

如果您只想查看一架战斗机的结果,请添加:
   - WHERE participant.fighter_id = :fighter_id


更好的是,使用触发器保持此类数据的最新状态。这样你就不需要一次又一次地计算它。