子查询以获得更快的结果

时间:2016-10-01 00:21:08

标签: mysql optimization subquery left-join

我有这个查询,在mysql数据库上花了我超过117秒。

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

如何使用子查询来优化它?

3个字段是全文:

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`);

网站中只有一个搜索输入可以在不同的表用户字段中进行搜索。

如果限制是例如LIMIT 0,50,则查询将在不到3秒的时间内运行,但是当LIMIT增加时,查询变得非常慢。

感谢。

2 个答案:

答案 0 :(得分:0)

使用单个FULLTEXT索引:

FULLTEXT(user_email, user_firstname, user_lastname)

并将3场比赛改为一场:

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

这是另一个问题:ORDER BY ... DESC LIMIT 1400, 50。阅读pagination via OFFSET的罪恶。这有一个解决方法,但我怀疑它是否适用于你的陈述。

你真的有成千上万的用户匹配文字吗?有人(搜索引擎机器人除外)是否真正翻过29页?想想真正拥有如此冗长的用户界面是否有意义。

第3期。考虑" lazy eval"。也就是说,首先找到用户ID,然后加入回usersusers_oauth以获取其余列。它将是一个SELECT,其中MATCH位于派生表中,然后JOIN位于两个表中。如果ORDER BYLIMIT可以在派生表中,则可能是一个很大的胜利。

请说明每列属于哪一个表 - 由于不知道日期列,我的最后一段是不精确的。

<强>更新

在您的第二次尝试中,您添加了OR,这会大大减慢速度。让我们把它变成UNION以试图避免新的减速。首先让我们调试UNION

( 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
)

通过分别计算每个SELECT来测试它。如果其中一个仍然很慢,那么让我们专注于它。然后测试UNION。 (这是使用mysql命令行工具可能比PHP更方便的情况。)

通过拆分,每个SELECT都可以使用最佳索引。 UNION有一些开销,但可能低于OR的低效率。

现在让我们在users_oauth中弃牌。

首先,您似乎错过了一个非常重要的INDEX(oauth_user_id)。加上!

现在让我们把它们放在一起。

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

答案 1 :(得分:0)

是的@Rick

我将索引全文更改为:

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

现在有一些php条件,$ _POST [&#39;搜索&#39;]可以为空:

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 )) ;

users_oauth表有一个包含user_id的列:

表用户:

    +--------------------------+-----------------+------+-----+---------+----------------+
    | 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      
    +--------------------------+-----------------+------+-----+---------+----------------+

表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    |                |
+----------------------+-----------------+------+-----+---------+----------------+

左连接很长,请求需要3秒,0,0158秒。

为每50行创建一个sql请求会更快。

子查询会更快吗?如何使用子查询来创建它?

由于