2012-11-05 166 views
0

我有一個完美的查詢(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 
+0

一個大問題是你的戰鬥表包含兩個戰鬥機ID。你需要用fight_id和fighter_id創建一個規範化查找表,也許稱爲參與者;每場戰鬥兩排。 – MatBailie

回答

0

使用a.id IN (b.a_id, b.b_id)永遠將是高性能,您將不能創建以適應查詢的索引。你應該規範你的結構的這個方面。

我提出以下修改您的模式:

  • 添加fight_date_timefights
  • 移動fighter_afighter_b出一個participicants表
    • 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


更妙的是,保持這種數據的瞭解最新的觸發器。這樣你就不需要一次又一次地計算它。

+0

您可以通過添加'fight_date_time'字段並創建'participant'表來測試。你不需要從你的模式中刪除任何東西來測試它。 – MatBailie

+0

好聽起來不錯。我會放棄並報告。謝謝你的幫助! – zen

+0

好的,我得到了所有數據的新參與者表,並在戰鬥表中添加了一個fight_date字段。我在哪裏插入WHERE子句,因爲我只想檢索一個戰士的數據。 – zen