子查询以获得更快的结果

Subquery for faster result

我的这个查询在 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`);

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

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

谢谢。

使用单个 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 页吗?想想真的这么啰嗦有没有意义UI.

还有第 3 期。考虑 "lazy eval"。也就是说,首先找到用户 ID, 然后 连接回 usersusers_oauth 以获取其余列。它将是单个 SELECT,在派生的 table 中带有 MATCH,然后 JOIN 到两个 table。如果ORDER BY一个LIMIT能在派生table,那就大获全胜了。

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

更新

在您的第二次尝试中,您添加了 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

是@Rick

我将索引全文更改为:

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

现在有一些php条件,$_POST['search']可以为空:

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 table 有一列 user_id:

Table 用户:

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

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

Left Join 很长,请求耗时 3 秒,耗时 0,0158 秒。

每 50 行发出一个 sql 请求会更快。

使用子查询会更快吗?如何使用子查询实现?

谢谢