1

永遠にかかるランキングのためにクエリを最適化する必要があります(クエリ自体は機能しますが、それがひどいことはわかっています。かなりの数のレコードで試してみたところ、タイムアウトが発生しました)。

モデルについて簡単に説明します。player、team、player_team の 3 つのテーブルがあります。チームに所属できる選手がいます。当然のことながら、プレーヤーはプレーヤー テーブルに格納され、チームはチームに格納されます。私のアプリでは、各プレイヤーはいつでもチームを切り替えることができ、ログを維持する必要があります。ただし、プレイヤーは一度に 1 つのチームにのみ所属すると見なされます。プレーヤーの現在のチームは、最後に参加したチームです。

選手とチームの構造は関係ないと思います。それぞれに id 列 PK があります。player_team には次のものがあります。

id          (PK)
player_id   (FK -> player.id)
team_id     (FK -> team.id)

現在、各チームには、参加した各プレイヤーのポイントが割り当てられています。そこで、今、プレイヤー数が最も多い最初の N チームのランキングを取得したいと考えています。

私の最初のアイデアは、最初に player_team から現在のプレーヤーを取得することでした (つまり、各プレーヤーの 1 つのレコード トップです。このレコードはプレーヤーの現在のチームでなければなりません)。簡単な方法を見つけることができませんでした (GROUP BY player_team.player_id HAVING player_team.id = MAX(player_team.id) を試しましたが、うまくいきませんでした。

うまくいかなかった多くのクエリを試しましたが、なんとかこれを機能させることができました。

SELECT 
    COUNT(*) AS total,
    pt.team_id,
    p.facebook_uid AS owner_uid, 
    t.color 
FROM 
    player_team pt 
JOIN player p ON (p.id = pt.player_id)  
JOIN team t ON (t.id = pt.team_id) 
WHERE 
    pt.id IN (
        SELECT max(J.id) 
        FROM player_team J 
        GROUP BY J.player_id
    )  

GROUP BY 
    pt.team_id 
ORDER BY 
    total DESC 
LIMIT 50            

私が言ったように、それは機能しますが、見た目は非常に悪く、パフォーマンスも悪いので、もっと良い方法があるはずです. これを最適化するためのアイデアはありますか?

ちなみに私はmysqlを使っています。

前もって感謝します

説明を追加します。(申し訳ありませんが、適切にフォーマットする方法がわかりません)

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
1   PRIMARY     t   ALL     PRIMARY     NULL    NULL    NULL    5000    Using temporary; Using filesort
1   PRIMARY     pt  ref     FKplayer_pt77082,FKplayer_pt265938,new_index    FKplayer_pt77082    4   t.id    30  Using where
1   PRIMARY     p   eq_ref  PRIMARY     PRIMARY     4   pt.player_id    1
2   DEPENDENT SUBQUERY  J   index   NULL    new_index   8   NULL    150000  Using index
4

5 に答える 5

2

これを試して:

SELECT  t.*, cnt
FROM    (
        SELECT  team_id, COUNT(*) AS cnt
        FROM    (
                SELECT  player_id, MAX(id) AS mid
                FROM    player_team
                GROUP BY
                        player_id
                ) q
        JOIN    player_team pt
        ON      pt.id = q.mid
        GROUP BY
                team_id
        ) q2
JOIN    team t
ON      t.id = q2.team_id
ORDER BY
        cnt DESC
LIMIT 50

player_team (player_id, id)これを高速に動作させるには、(この順序で) にインデックスを作成します。

于 2010-05-07T13:44:47.927 に答える
2

それを殺しているのはサブクエリです-テーブルにcurrentフィールドを追加するplayer_teamと、現在の場合は値= 1、古い場合は0を指定すると、次のようにするだけでこれを大幅に簡素化できます。

SELECT 
    COUNT(*) AS total,
    pt.team_id,
    p.facebook_uid AS owner_uid, 
    t.color 
FROM 
    player_team pt 
JOIN player p ON (p.id = pt.player_id)  
JOIN team t ON (t.id = pt.team_id) 
WHERE 
    player_team.current = 1 
GROUP BY 
    pt.team_id 
ORDER BY 
    total DESC 
LIMIT 50  

同じ関係のテーブルに複数のエントリがあり、player_teamどれが「現在の」レコードであるかを区別する唯一の方法は、2 つ (またはそれ以上) の行を比較することであり、悪い習慣だと思います。私は以前にこの状況に陥ったことがあり、それを機能させるために実行する必要がある回避策は、実際にパフォーマンスを低下させます。where current=1簡単なルックアップ (この場合は) を実行するか、履歴データを完全に別のテーブルに移動する (状況によってはやり過ぎかもしれません) ことによって、現在の行を確認できる方がはるかに優れています。

于 2010-05-07T13:33:30.510 に答える
1

MySQL のより複雑なクエリを 2 つの部分に分割する必要がある場合があります。

最初の部分は必要なデータを一時テーブルにプルし、2 番目の部分は作成されたデータセットを操作しようとするクエリになります。これを行うと、パフォーマンスが大幅に向上することは間違いありません。

于 2010-05-07T13:31:55.223 に答える
0

列「last_playteam_id」をプレーヤー テーブルに追加し、プレーヤーが player_team テーブルの pk でチームを変更するたびに更新できます。

次に、これを行うことができます:

SELECT 
    COUNT(*) AS total,
    pt.team_id,
    p.facebook_uid AS owner_uid, 
    t.color 
FROM 
    player_team pt 
JOIN player p ON (p.id = pt.player_id)  and p.last_playteam_id = pt.id
JOIN team t ON (t.id = pt.team_id) 
GROUP BY 
    pt.team_id 
ORDER BY 
    total DESC 
LIMIT 50   

古い player_team 行を current=0 に更新する必要がないため、これが最も高速です。

代わりに列「last_team_id」を追加して、そこに現在のチームを保持することもできます。上記のクエリで最速の結果が得られますが、他のクエリではあまり役に立たない可能性があります.

于 2010-05-07T14:55:15.040 に答える
0

これにより、サイズ順に並べられた色で現在のチームが取得されます。

  SELECT team_id, COUNT(player_id) c AS total, t.color 
    FROM player_team pt JOIN teams t ON t.team_id=pt.team_id  
    GROUP BY pt.team_id WHERE current=1
    ORDER BY pt.c DESC
    LIMIT 50;

しかし、どのプレーヤーがチームの所有者と見なされるかについての条件を指定していません。現在のクエリは、そのプレーヤーが実際の所有者であるためではなく、グループ化のために、1 人のプレーヤーを owner_id として任意に表示しています。player_team テーブルに「所有者」列が含まれている場合、上記のクエリを所有者のクエリに結合できます。何かのようなもの:

SELECT o.facebook_uid, a.team_id, a.color, a.c
FROM player_teams pt1 
  JOIN players o ON (pt1.player_id=o.player_id AND o.owner=1)
  JOIN (...above query...) a
    ON a.team_id=pt1.team_id;
于 2010-05-07T13:49:19.590 に答える