记住带布尔short-circuit evaluation的优势:
SELECT COUNT(*)
FROM messages
join emails ON emails.id = messages.emailid
WHERE ownership = 32 AND message LIKE '%word%'
该过滤器由ownership
则计算LIKE
谓语前。总是把你的便宜的表情放在左边。
此外,我同意@Martin Smith和@MJB,您应该考虑使用MySQL的FULLTEXT
索引来加快速度。
回复您的评论和其它信息,这里的一些分析:
explain SELECT COUNT(*) FROM ticket WHERE category IN (1)\G
id: 1
select_type: SIMPLE
table: ticket
type: ref
possible_keys: category
key: category
key_len: 4
ref: const
rows: 1
Extra: Using index
的说明“使用索引”是一件好事,看看,因为这意味着它能够满足查询只需读取索引数据结构,甚至不涉及表格的数据。这肯定会跑得非常快。
explain SELECT COUNT(*) FROM ticket_subject WHERE subject LIKE '%about%'\G
id: 1
select_type: SIMPLE
table: ticket_subject
type: ALL
possible_keys: NULL <---- no possible keys
key: NULL
key_len: NULL
ref: NULL
rows: 1
Extra: Using where
这表明没有可能有益于通配符LIKE
谓词的可能键。它使用WHERE子句中的条件,但必须通过运行表扫描来评估它。
explain SELECT COUNT(*) FROM ticket LEFT JOIN ticket_subject
ON (ticket_subject.ticketid = ticket.id)
WHERE category IN (1)
AND ticket_subject.subject LIKE '%about%'\G
id: 1
select_type: SIMPLE
table: ticket
type: ref
possible_keys: PRIMARY,category
key: category
key_len: 4
ref: const
rows: 1
Extra: Using index
id: 1
select_type: SIMPLE
table: ticket_subject
type: ref
possible_keys: ticketid
key: ticketid
key_len: 4
ref: test.ticket.id
rows: 1
Extra: Using where
同样,访问票表快,但是这由LIKE
状态所发生的表扫描宠坏了。
ALTER TABLE ticket_subject ENGINE=MyISAM;
CREATE FULLTEXT INDEX ticket_subject_fulltext ON ticket_subject(subject);
explain SELECT COUNT(*) FROM ticket JOIN ticket_subject
ON (ticket_subject.ticketid = ticket.id)
WHERE category IN (1) AND MATCH(ticket_subject.subject) AGAINST('about')
id: 1
select_type: SIMPLE
table: ticket
type: ref
possible_keys: PRIMARY,category
key: category
key_len: 4
ref: const
rows: 1
Extra: Using index
id: 1
select_type: SIMPLE
table: ticket_subject
type: fulltext
possible_keys: ticketid,ticket_subject_fulltext
key: ticket_subject_fulltext <---- now it uses an index
key_len: 0
ref:
rows: 1
Extra: Using where
你永远不会做出LIKE
表现良好。看到我的介绍Practical Full-Text Search in MySQL。
回复您的评论:好吧,我已经做了类似规模的数据集一些实验(在用户和徽章在堆栈溢出数据表转储:-)。这里是我发现的:
select count(*) from users
where reputation > 50000
+----------+
| count(*) |
+----------+
| 37 |
+----------+
1 row in set (0.00 sec)
这真的很快,因为我在声望列上有一个索引。
id: 1
select_type: SIMPLE
table: users
type: range
possible_keys: users_reputation_userid_displayname
key: users_reputation_userid_displayname
key_len: 4
ref: NULL
rows: 37
Extra: Using where; Using index
select count(*) from badges
where badges.creationdate like '%06-24%'
+----------+
| count(*) |
+----------+
| 1319 |
+----------+
1 row in set, 1 warning (0.63 sec)
这是预料之中的,因为该表有700k行,它必须执行表扫描。现在让我们来加入:
select count(*) from users join badges using (userid)
where users.reputation > 50000 and badges.creationdate like '%06-24%'
+----------+
| count(*) |
+----------+
| 19 |
+----------+
1 row in set, 1 warning (0.03 sec)
这似乎并不坏。这里的解释报告:
id: 1
select_type: SIMPLE
table: users
type: range
possible_keys: PRIMARY,users_reputation_userid_displayname
key: users_reputation_userid_displayname
key_len: 4
ref: NULL
rows: 37
Extra: Using where; Using index
id: 1
select_type: SIMPLE
table: badges
type: ref
possible_keys: badges_userid
key: badges_userid
key_len: 8
ref: testpattern.users.UserId
rows: 1
Extra: Using where
这似乎像它的使用索引智能化的加入,它可以帮助我有包括用户ID和美誉度一个复合索引。请记住,MySQL只能为每个表使用一个索引,因此为需要执行的查询定义正确的复合索引非常重要。
回复您的评论:OK,我已经试过这其中口碑> 5000,并在信誉> 500,并在信誉> 50.这些应该匹配更大的一组用户。
select count(*) from users join badges using (userid)
where users.reputation > 5000 and badges.creationdate like '%06-24%'
+----------+
| count(*) |
+----------+
| 194 |
+----------+
1 row in set, 1 warning (0.27 sec)
select count(*) from users join badges using (userid)
where users.reputation > 500 and badges.creationdate like '%06-24%'
+----------+
| count(*) |
+----------+
| 624 |
+----------+
1 row in set, 1 warning (0.93 sec)
select count(*) from users join badges using (userid)
where users.reputation > 50 and badges.creationdate like '%06-24%'
--------------
+----------+
| count(*) |
+----------+
| 1067 |
+----------+
1 row in set, 1 warning (1.72 sec)
的解释报告是在所有情况下是相同的,但如果查询的用户表中寻找更多的匹配行,那么它自然要评估对在徽章表了很多更多的匹配行的LIKE
谓语。
这确实是有一些成本做一个加入。有点惊人的是,它非常昂贵。但是如果你使用索引,这可以被缓解。
我知道你说你不能使用索引的查询,但也许是时候考虑创建您的原始列的数据的一些变换形式冗余列,所以你可以指数吧。在上面的示例中,我可能会创建一个列creationdate_day
并从DAYOFYEAR(creationdate)
填充它。
这里就是我的意思是:
ALTER TABLE Badges ADD COLUMN creationdate_day SMALLINT;
UPDATE Badges SET creationdate_day = DAYOFYEAR(creationdate);
CREATE INDEX badge_creationdate_day ON Badges(creationdate_day);
select count(*) from users join badges using (userid)
where users.reputation > 50 and badges.creationdate_day = dayofyear('2010-06-24')
+----------+
| count(*) |
+----------+
| 1067 |
+----------+
1 row in set, 1 warning (0.01 sec) <---- not too shabby!
这里的解释报告:
id: 1
select_type: SIMPLE
table: badges
type: ref
possible_keys: badges_userid,badge_creationdate_day
key: badge_creationdate_day <---- here is our new index
key_len: 3
ref: const
rows: 1318
Extra: Using where
id: 1
select_type: SIMPLE
table: users
type: eq_ref
possible_keys: PRIMARY,users_reputation_userid_displayname
key: PRIMARY
key_len: 8
ref: testpattern.badges.UserId
rows: 1
Extra: Using where
所以基本上你试图迫使它通过EMAILID了'消息LIKE'之前做的过滤器%word%''有点发生?或者这正是你想要阻止的事情? – 2010-06-23 12:37:08
是的,那正是我想要发生的事情。可能它正在发生,mySQL在取得这些结果(33,000)时会很慢,然后搜索它们。但似乎奇怪的是,在索引列中找到ticket_subject表中的33,000个结果要比在非索引列上在该表中搜索300,000个结果要慢。 – 2010-06-23 23:09:10