2016-10-01 90 views
-2

我有这个查询,这需要我超过117秒的mysql数据库。子查询更快的结果

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增加时查询变得非常缓慢。

谢谢。

+1

你能用这个查询运行EXPLAIN并发布结果吗? – Besi

+1

并确定列来自哪些表。 –

+0

您是否在全部搜索字段上创建了全文索引? – Shadow

回答

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期。考虑“懒惰的评估”。也就是说,首先找到用户ID,然后然后返回到usersusers_oauth以获取其余列。这将是一个单独的SELECTMATCH在派生表中,然后JOIN到两个表中。如果ORDER BYLIMIT可以在派生表中,那可能是一个巨大的胜利。

请指出每一列属于哪一个表 - 我的最后一段由于不知道日期列而不准确。

更新

在你的第二次尝试,你添加OR,从而大大减缓下来。让我们把它变成UNION,以避免新的放缓。首先,让我们调试UNION:通过单独计时每SELECT

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

测试它。如果其中一个仍然很慢,那么让我们来关注它。然后测试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 
+0

谢谢瑞克,我加了一个表格回复列 –

+0

查看更新..... –

+0

你是怎么猜到的?事实上,我忘了'INDEX(oauth_user_id)',现在这个请求需要我0.6615秒:) 谢谢你里克,现在我明白了UNION ALL以及如何管理。 :) –

0

是@Rick

我改变了索引全文于:

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

而且现在有一些PHP条件,$ _ POST [ '搜索']可以为空:

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秒wihtout。

对每50行进行一次sql请求会更加迅速。

子查询会更快吗?如何使它与子查询?

谢谢