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_aして外に出ますfighter_b
    • fight_idfighter_id- 正規化 - 戦闘ごとに 2 行


以下は、戦闘ごとに 2 つのレコード(ファイターごとに 1 つ)を作成しますが、対戦相手もリストし、両方のレコードが戦闘に参加します。

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


1 人の選手だけの結果を見たい場合は、次を追加します。WHERE participant.fighter_id = :fighter_id


さらに良いことに、トリガーを使用してこの種のデータを最新の状態に保ちます。そうすれば、何度も何度も計算する必要はありません。

于 2012-11-05T07:19:49.123 に答える