我有这个查询,在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增加时,查询变得非常慢。
感谢。
答案 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,然后加入回users
和users_oauth
以获取其余列。它将是一个SELECT
,其中MATCH
位于派生表中,然后JOIN
位于两个表中。如果ORDER BY
和LIMIT
可以在派生表中,则可能是一个很大的胜利。
请说明每列属于哪一个表 - 由于不知道日期列,我的最后一段是不精确的。
<强>更新强>
在您的第二次尝试中,您添加了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请求会更快。
子查询会更快吗?如何使用子查询来创建它?
由于