2016-09-24 9 views
0

私はプロパティのWebサイトを管理しています。私は禁止されたユーザー(小さなテーブル)と各ユーザーが表示する各リストを追跡するadvert_viewsと呼ばれるテーブルを持っています(現在1.3mラインと成長)。 advert_viewsテーブルalsioは、表示されたすべての広告のIPアドレスを記録します)。mysqlを使用して大きなテーブルをクエリする

禁止されたユーザーが使用しているIPアドレスを取得し、禁止されたユーザーのいずれかが新しいアカウントを開いたかどうかを確認します。

SELECT adviews.user_id AS 'banned user_id', 
     adviews.client_ip AS 'IPs used by banned users', 
     adviews2.user_id AS 'banned users that opened a new account' 
FROM banned_users 
LEFT JOIN users on users.email_address = banned_users.email_address #since I don't store the user_id in banned_users 
LEFT JOIN advert_views adviews ON adviews.user_id = users.id AND adviews.user_id IS NOT NULL # users may view listings when not logged in but they have restricted access to the information on the listing 
LEFT JOIN (SELECT client_ip, 
        user_id 
        FROM advert_views 
        WHERE user_id IS NOT NULL 
       ) adviews2 
       ON adviews2.client_ip = adviews.client_ip 
WHERE banned_users.rec_status = 1 and adviews.user_id <> adviews2.user_id 
GROUP BY adviews2.user_id 

私は以下のとおりadvert_viewsテーブル上のインデックスとユーザーテーブルを適用:私は、次のクエリを実行した

enter image description here

私のクエリを実行する時間半かかります。クエリ速度を改善する方法はありますか?

ありがとうございます! Chris

+0

クエリプラン –

+3

を表示してください。これを行うには、IPアドレスがかなり危険です。多くのIPアドレスは多くの異なる人々の間で共有されています。 –

+0

IPアドレスを再利用するために期限を設けてください。人々は新しいIPアドレスを要求することができ、これは一般的な禁止方法の回避方法です。より有用なのは、現在のIPアドレスの逆引きです。ホスティングサービスが同じ顧客IDを指していることがわかります。私はそうする; - /。明らかに、匿名メソッドのいずれかが使用されている場合、このメソッドは役に立たない。 –

答えて

0

まず第一に、テーブルを外部結合するのはなぜですか。またはそれ以上:なぜあなたはを外にテーブルを結合するを試してみませんか?左結合は、一致がない場合でもテーブルからデータを取得することを意味します。しかし、結果には、すべての値がNULLの行が含まれる可能性があります。 (where節のadviews.user_id <> adviews2.user_idは外部結合されたすべての行を閉じます。)DBMSに必要以上の処理を加えないでください。内部結合を必要とする場合は、外部結合を行わないでください。 (実行時間の差は巨大ではありませんが)

次:banned_usersから選択しますが、存在を確認するためにのみ使用します。あなたはこれをしてはいけません。代わりにEXISTSまたはIN句を使用してください。 (これはこれはおそらく、物事をスピードアップしません。読みやすくするために、重複する結果を生成しないようにするために主にある。)

SELECT av1.user_id AS 'banned user_id', 
     av2.client_ip AS 'IPs used by banned users', 
     av2.user_id AS 'banned users that opened a new account' 
FROM adviews av1 
JOIN adviews av2 ON av2.client_ip = av1.client_ip AND av2.user_id <> av1.user_id 
WHERE av1.user_id IN 
(
    SELECT user_id 
    FROM users 
    WHERE email_address IN (select email_address from banned_users where rec_status = 1) 
) 
GROUP BY av2.user_id; 

あなたが参加して、内側IN句を置き換えることができます。これは主に個人的な好みの問題ですが、過去にMySQLがIN節でうまく機能しないことがあったため、多くの人が代わりに参加する習慣を作りました。

WHERE av1.user_id IN 
(
    SELECT u.user_id 
    FROM users u 
    JOIN banned_users bu ON bu.email_address = u.email_address 
    WHERE bu.rec_status = 1 
) 

最後にGROUP BY節を削除することを検討してください。再利用user_idごとに結果を1行に減らし、関連する禁止されたuser_idの1つを表示します(複数の場合に任意に選択されます)。私はあなたのテーブルを知らない。 reusing user_idごとに多くのレコードを取得していますか?そうでない場合は、節を削除します。

はインデックスに関しては、私はお勧め:

  • banned_users(rec_status、EMAIL_ADDRESS)
  • ユーザー(EMAIL_ADDRESS、USER_ID)
  • adviews(user_idは、CLIENT_IP)
  • adviews(CLIENT_IP、USER_ID)
+0

こんにちはThorstenさん、ありがとうございました!本当に感謝しております。私はあなたの文法だけには到着しませんでした。私が 'banned_users'と 'users'テーブルを結合した理由は、user_idをbanned_usersテーブルに保存しないためです。私はあなたのポイントreグループを取る - しかし、時には複数のuser_idがクライアントipに添付されていることに気づいた。あなたが提案したインデックスを追加します。インデックスがなければ、実行時間は3分で改善されました:S –

+0

ああ、そうです、私は 'banned_users'に' user_id'がないことを逃しました。ごめんなさい。したがって、サブクエリ内のそのテーブルに参加するか、入れ子になった 'IN'句を使用する必要があります('どこからav_user_idを選んでください(どこからrec_status = 1のbanned_usersからemail_addressを選択するのかuser_idを選択してください)。 –

+0

私はそれに応じて私の答えを更新しました。 –

関連する問題