子查询以获得更快的结果
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, 然后 连接回 users
和 users_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 请求会更快。
使用子查询会更快吗?如何使用子查询实现?
谢谢
我的这个查询在 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, 然后 连接回 users
和 users_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 请求会更快。
使用子查询会更快吗?如何使用子查询实现?
谢谢