記住帶布爾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