2016-09-24 3 views
0

Ich verwalte eine Immobilien-Website. Ich habe eine Tabelle mit gesperrten Benutzern (kleine Tabelle) und eine Tabelle namens advert_views, die jeden Eintrag verfolgt, den jeder Benutzer sieht (derzeit 1,3 Millionen Zeilen und wachsend). Die Tabelle advert_views berücksichtigt die IP-Adresse jeder angezeigten Anzeige.Abfrage einer großen Tabelle mit mysql

Ich möchte die von den gesperrten Benutzern verwendeten IP-Adressen abrufen und überprüfen, ob einer dieser gesperrten Benutzer neue Konten eröffnet hat. Ich lief die folgende Abfrage:

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 

ich einen Index für die advert_views Tabelle angelegt und die Benutzer-Tabelle nach unten:

enter image description here

Meine Abfrage dauert eine halbe Stunde auszuführen. Gibt es eine Möglichkeit, meine Abfragegeschwindigkeit zu verbessern?

Danke! Chris

+0

Bitte zeigen Sie den Abfrageplan –

+3

IP-Adressen sind eine ziemlich gefährliche Möglichkeit, dies zu tun. Viele IP-Adressen werden von vielen verschiedenen Personen gemeinsam genutzt. –

+0

Sollten Sie ein Zeitlimit für die Wiederverwendung der IP-Adresse verwenden? Benutzer können eine neue IP-Adresse anfordern. Dies ist eine gängige Methode, um Verbotsversuche zu vermeiden. Was nützlicher ist, ist eine umgekehrte Suche ihrer aktuellen IP-Adresse. Möglicherweise stellen Sie fest, dass der Hosting-Service auf dieselbe Kundennummer verweist.Meins tut; - /. Wenn eine der anonymen Methoden verwendet wird, ist diese Methode natürlich nutzlos. –

Antwort

0

Vor allem: Warum treten Sie äußere die Tabellen? Oder besser: Warum versuchen Sie zu Outer treten die Tabellen? Ein linker Join soll Daten aus einer Tabelle erhalten, auch wenn keine Übereinstimmung vorhanden ist. Aber dann könnten Ihre Ergebnisse Zeilen mit allen Werten enthalten, die null sind. (Dies passiert jedoch nicht, weil adviews.user_id <> adviews2.user_id in Ihrer WHERE-Klausel alle extern verknüpften Zeilen ablehnt.) Geben Sie dem DBMS nicht mehr Arbeit als nötig. Wenn Sie innere Joins möchten, dann verbinden Sie sich nicht mit Outer. (Obwohl der Unterschied in der Ausführungszeit nicht groß ist.)

Weiter: Sie wählen aus banned_users, aber Sie verwenden es nur, um Existenz zu überprüfen. Du solltest das nicht tun. Verwenden Sie stattdessen einen EXISTS oder IN Klausel. (Dies ist in erster Linie für die Lesbarkeit und um nicht doppelte Ergebnisse zu produzieren. Dies wird wahrscheinlich Dinge nicht beschleunigen.)

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; 

Sie können die innere IN Klausel mit einem Join ersetzen. Es ist hauptsächlich eine Frage der persönlichen Präferenz, aber es ist auch so, dass MySQL in der Vergangenheit unter IN Klauseln manchmal nicht gut abgeschnitten hat, so dass viele Leute es sich zur Gewohnheit machten, stattdessen beizutreten.

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 
) 

Endlich in Betracht ziehen, die Klausel GROUP BY zu entfernen. Sie reduziert Ihre Ergebnisse auf eine Zeile, indem Sie user_id erneut verwenden und eine der zugehörigen blockierten user_ids anzeigen (willkürlich gewählt, falls es mehrere gibt). Ich kenne deine Tische nicht. Erhalten Sie viele Datensätze, wenn Sie user_id wiederverwenden? Wenn nicht, entfernen Sie die Klausel.

In Bezug auf Indizes Ich schlage vor:

  • banned_users (rec_status, email_address)
  • Benutzer (email_address, user_id)
  • AdViews (user_id, client_ip)
  • AdViews (client_ip, user_id)
+0

Hallo Thorsten, vielen Dank für die Hilfe! Ich schätze wirklich. Ich wäre niemals allein zu deiner Syntax gekommen. Der Grund, warum ich "banned_users" mit der Tabelle "users" beigetreten bin, ist, dass ich die user_id nicht in der Tabelle "banned_users" aufbewahre. Ich nehme Ihren Punkt re Gruppe von - aber ich habe festgestellt, dass es mehr als eine user_id manchmal an eine Client-IP angehängt ist. Ich werde die von Ihnen vorgeschlagenen Indizes hinzufügen. Ohne die Indizes hat sich die Laufzeit um 3 Minuten verbessert: S –

+0

Ah, du hast recht, ich habe übersehen, dass 'banned_users' keine' user_id' hat. Es tut uns leid. Sie müssten also der Tabelle in der Unterabfrage beitreten oder eine verschachtelte 'IN'-Klausel verwenden (' wo av.user_id in (wählen Sie user_id von Benutzern aus, in denen email_address (wählen Sie email_address von banned_users wobei rec_status = 1)) Der Index 'banned_users (rec_status, user_id)' Ich schlage 'banned_users (rec_status, email_address)' und 'user (email_address, user_id)' dann vor. –

+0

Ich habe meine Antwort entsprechend aktualisiert. –

Verwandte Themen