2016-10-01 4 views
-2

Ich habe diese Abfrage, die mich mehr als 117 Sekunden auf einer MySQL-Datenbank dauert.Unterabfrage für schnelleres Ergebnis

select users.*, users_oauth.* FROM users LEFT JOIN users_oauth ON users.user_id = users_oauth.oauth_user_id WHERE (

      (MATCH (user_email) AGAINST ('sometext')) OR 
      (MATCH (user_firstname) AGAINST ('sometext')) OR 
      (MATCH (user_lastname) AGAINST ('sometext'))) 

    ORDER BY user_date_accountcreated DESC LIMIT 1400, 50 

Wie kann ich eine Unterabfrage verwenden, um sie zu optimieren?

Die drei Felder sind Volltext:

ALTER TABLE `users` ADD FULLTEXT KEY `email_fulltext` (`user_email`); 
ALTER TABLE `users` ADD FULLTEXT KEY `firstname_fulltext` (`user_firstname`); 
ALTER TABLE `users` ADD FULLTEXT KEY `lastname_fulltext` (`user_lastname`); 

Es gibt nur eine Sucheingabe in einer Website in verschiedenen Tabellen Benutzer Felder zu suchen.

Wenn das Limit beispielsweise LIMIT 0,50 ist, wird die Abfrage in weniger als 3 Sekunden ausgeführt, aber wenn die LIMIT erhöht, wird die Abfrage sehr langsam.

Danke.

+1

Können Sie ein EXPLAIN mit dieser Abfrage ausführen und die Ergebnisse veröffentlichen? – Besi

+1

Und identifizieren, aus welchen Tabellen die Spalten stammen. –

+0

Haben Sie einen Volltextindex für die Felder, nach denen gesucht wird, gefunden? Gegen ...? – Shadow

Antwort

0

einen einzigen FULLTEXT- Index Verwendung:

FULLTEXT(user_email, user_firstname, user_lastname) 

und die 3 Ursachen ändern man nur:

MATCH (user_email, user_firstname, user_lastname) AGAINST ('sometext') 

Hier ist ein weiteres Problem: ORDER BY ... DESC LIMIT 1400, 50. Lies über die Übel von pagination via OFFSET. Das hat einen Workaround, aber ich bezweifle, ob es für Ihre Aussage gelten würde.

Haben wirklich Tausende von Benutzern den Text? Führt jemand (außer einem Suchmaschinenroboter) wirklich 29 Seiten durch? Überlegen Sie, ob es sinnvoll ist, eine so langatmige Benutzeroberfläche zu haben.

Und eine dritte Ausgabe. Betrachten Sie "Lazy Eval". Das heißt, finden Sie zuerst die Benutzer-IDs, , dann verbinden Sie sich zurück zu users und users_oauth, um den Rest der Spalten zu erhalten. Es wäre eine einzige SELECT mit der MATCH in einer abgeleiteten Tabelle, dann JOIN zu den beiden Tabellen. Wenn der ORDER BY ein LIMIT in der abgeleiteten Tabelle sein kann, könnte es ein großer Gewinn sein.

Bitte geben Sie an, zu welcher Tabelle jede Spalte gehört - mein letzter Absatz ist ungenau, weil ich nichts über die Datumsspalte weiß.

aktualisieren

In Ihrem zweiten Versuch hinzugefügt Sie OR, die Dinge stark verlangsamt. Lassen Sie uns das in eine UNION verwandeln, um zu versuchen, die neue Verlangsamung zu vermeiden. Lassen Sie uns zunächst die UNION debuggen:

(SELECT * -- no mention of oauth columns 
    FROM users -- No JOIN 
    WHERE users.user_id LIKE ... 
    ORDER BY user_id DESC 
    LIMIT 0, 50 
) 
UNION ALL 
(SELECT * -- no mention of oauth columns 
    FROM users 
    WHERE MATCH ... 
    ORDER BY user_id DESC 
    LIMIT 0, 50 
) 

-Test durch jedes SELECT separat Timing. Wenn einer davon noch langsam ist, dann konzentrieren wir uns darauf. Dann testen Sie die UNION. (Dies ist ein Fall, bei dem die Verwendung des Befehlszeilentools mysql praktischer ist als PHP.)

Durch Aufteilen kann jeder SELECT einen optimalen Index verwenden. Die UNION hat einige Overhead, aber möglicherweise weniger als die Ineffizienz von OR.

Jetzt lass uns users_oauth einklappen.

Zunächst scheinen Sie eine sehr wichtige INDEX(oauth_user_id) fehlt. Fügen Sie das hinzu!

Jetzt wollen wir sie zusammensetzen.

SELECT u.* 
    FROM (.... the entire union query ...) AS u 
    LEFT JOIN users_oauth ON users.user_id = users_oauth.oauth_user_id 
    ORDER BY user_id DESC -- yes, repeat 
    LIMIT 0, 50    -- yes, repeat 
+0

Dank Rick, fügte ich eine Antwort mit Tabellen Spalten –

+0

Siehe Update ..... –

+0

Wie hast du es erraten? Tatsächlich habe ich den 'INDEX (oauth_user_id)' vergessen und jetzt dauert diese Anfrage 0.6615 Sekunden :) Danke dir Rick, jetzt verstehe ich die UNION ALL und wie man damit umgeht. :) –

0

Ja @Rick

änderte ich den Index Volltext in:

ALTER TABLE `users` 
    ADD FULLTEXT KEY `fulltext_adminsearch` (`user_email`,`user_firstname`,`user_lastname`); 

Und jetzt gibt es einige PHP-Bedingungen, $ _POST [ 'Suche'] kann leer sein:

if(!isset($_POST['search'])) { 
    $searchId = '%' ; 
} else { 
    $searchId = $_POST['search'] ; 
} 
$searchMatch = '+'.str_replace(' ', ' +', $_POST['search']); 
$sqlSearch = $dataBase->prepare(
     'SELECT users.*, users_oauth.* 
      FROM users 
      LEFT JOIN users_oauth ON users.user_id = users_oauth.oauth_user_id 
      WHERE ( users.user_id LIKE :id OR 
        (MATCH (user_email, user_firstname, user_lastname) 
          AGAINST (:match IN BOOLEAN MODE))) 
      ORDER BY user_id DESC LIMIT 0,50') ; 

$sqlSearch->execute(array('id' => $searchId, 
          'match' => $searchMatch)) ; 

Die Tabelle users_oauth hat eine Spalte mit user_id:

Tabelle Benutzer:

+--------------------------+-----------------+------+-----+---------+----------------+ 
    | Field     | Type   | Null | Key | Default | Extra   | 
    +--------------------------+-----------------+------+-----+---------+----------------+ 
    | user_id     | int(8) unsigned | NO | PRI | NULL | auto_increment | 
    | user_activation_key  | varchar(40)  | YES |  | NULL |    | 
    | user_email    | varchar(40)  | NO | UNI |   |    | 
    | user_login    | varchar(30)  | YES |  | NULL |    | 
    | user_password   | varchar(40)  | YES |  | NULL |    | 
    | user_firstname   | varchar(30)  | YES |  | NULL |    | 
    | user_lastname   | varchar(50)  | YES |  | NULL |    | 
    | user_lang    | varchar(2)  | NO |  | en  
    +--------------------------+-----------------+------+-----+---------+----------------+ 

Tabelle users_oauth:

+----------------------+-----------------+------+-----+---------+----------------+ 
| Field    | Type   | Null | Key | Default | Extra   | 
+----------------------+-----------------+------+-----+---------+----------------+ 
| oauth_id    | int(8) unsigned | NO | PRI | NULL | auto_increment | 
| oauth_user_id  | int(8) unsigned | NO |  | NULL |    | 
| oauth_google_id  | varchar(30)  | YES | UNI | NULL |    | 
| oauth_facebook_id | varchar(30)  | YES | UNI | NULL |    | 
| oauth_windowslive_id | varchar(30)  | YES | UNI | NULL |    | 
+----------------------+-----------------+------+-----+---------+----------------+ 

Die LEFT JOIN ist lang, nimmt die Anfrage 3 Sekunden mit, 0,0158 Sekunden wihtout.

Es wäre schneller, eine SQL-Anfrage für jede 50 Zeilen zu machen.

Wäre es schneller mit einer Unterabfrage? Wie man es mit einer Unterabfrage macht?

Danke

Verwandte Themen