0

我有一个完美的查询(原始问题也有表结构),但它创建了一个临时表,不幸的是,由于数据量,它需要 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
4

1 回答 1

0

使用a.id IN (b.a_id, b.b_id)永远不会高效,您将无法创建适合查询的索引。你应该规范你结构的那个方面。

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

  • 添加fight_date_time到您的fights
  • 移动fighter_afighter_b移出参与者表
    • fight_id, fighter_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


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

于 2012-11-05T07:19:49.123 回答