帮我把SUBQUERY变成JOIN

Chr*_*eld 2 mysql join subquery

两张桌子.

电子邮件id(int10)| 所有权(int10)

消息emailid(int10)已索引| 消息(中等文本)

子查询(在mysql中很糟糕).

SELECT COUNT(*)FROM messages WHERE message LIKE'%word%'AND emailid IN(SELECT id FROM emails WHERE ownership = 32)


这里的用法是我在电子邮件上运行搜索(在上面的示例中显然简化了),它会生成一个包含3,000个电子邮件ID的列表.然后,我想对邮件进行搜索,因为我需要进行文本匹配 - 仅针对该邮件的3000封电子邮件.

对消息的查询是昂贵的(消息没有索引),但这很好,因为它只会检查几行.

思路:

i)加入.到目前为止,我对此的尝试都没有奏效,导致对消息表进行全表扫描(即未使用的emailid索引)ii)临时表.我认为这可行.iii)缓存客户端中的id并运行2个查询.这确实有效.不优雅.iv)子查询.mySQL子查询每次运行第二个查询,所以这不起作用.也许修复在mysql 6中.

好的,这是我到目前为止所拥有的.这些是实际的字段名称(我简化了一些问题).

查询:

SELECT COUNT(*) FROM ticket LEFT JOIN ticket_subject 
ON (ticket_subject.ticketid = ticket.id) 
WHERE category IN (1) 
AND ticket_subject.subject LIKE "%about%"
Run Code Online (Sandbox Code Playgroud)

结果:

1   SIMPLE  ticket  ref     PRIMARY,category    category    4   const   28874    
1   SIMPLE  ticket_subject  eq_ref  PRIMARY     PRIMARY     4   deskpro.ticket.id   1   Using where
Run Code Online (Sandbox Code Playgroud)

它需要0.41秒并返回113的计数(*).

运行:

SELECT COUNT (*) FROM ticket WHERE category IN (1)
Run Code Online (Sandbox Code Playgroud)

需要0.01秒才能找到33,000个结果.

运行

SELECT COUNT (*) FROM ticket_subject WHERE subject LIKE "%about%"
Run Code Online (Sandbox Code Playgroud)

需要0.14秒并找到1,300个结果.

票证表和ticket_subject表都有300,000行.

ticket_subject.ticketid和ticket.category上有一个索引.

我现在意识到使用LIKE语法是一个错误 - 因为它有点像FULLTEXT的红色鲱鱼.这不是问题.问题是:

1)表A - 非常快速的查询,在索引上运行.0.001秒2)表B - 中等到慢的查询,没有索引 - 进行全表扫描.0.1秒

这两个结果都很好.问题是我必须加入它们,搜索需要0.3秒; 这对我来说没有意义,因为表B上的组合查询的缓慢方面应该更快,因为我们现在只搜索该表的一小部分 - 即它不应该进行全表扫描,因为正在加入的字段on已编入索引.

Bil*_*win 8

记得利用布尔短路评估:

SELECT COUNT(*) 
FROM messages 
join emails ON emails.id = messages.emailid
WHERE ownership = 32 AND message LIKE '%word%'
Run Code Online (Sandbox Code Playgroud)

这会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
Run Code Online (Sandbox Code Playgroud)

注意"使用索引"是一件好事,因为它意味着它只需通过读取索引数据结构即可满足查询,甚至不会触及表的数据.这肯定会跑得很快.

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
Run Code Online (Sandbox Code Playgroud)

这表明没有可能的密钥可以使通配符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
Run Code Online (Sandbox Code Playgroud)

同样,访问票证表很快,但是由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
Run Code Online (Sandbox Code Playgroud)

你永远不会LIKE表现出色.请参阅我的演示文稿MySQL中的实用全文搜索.


重新评论:好的,我已经对类似大小的数据集(Stack Overflow数据转储中的用户和徽章表)进行了一些实验:-).这是我发现的:

select count(*) from users
where reputation > 50000

+----------+
| count(*) |
+----------+
|       37 |
+----------+
1 row in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)

这真的很快,因为我在声誉列上有一个索引.

           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)
Run Code Online (Sandbox Code Playgroud)

这是预期的,因为该表有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)
Run Code Online (Sandbox Code Playgroud)

这似乎并不那么糟糕.这是解释报告:

           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
Run Code Online (Sandbox Code Playgroud)

这看起来似乎是智能地为连接使用索引,它有助于我有一个复合索引,包括用户ID和声誉.请记住,MySQL每个表只能使用一个索引,因此为您需要的查询定义正确的复合索引非常重要.


重新评论:好的,我已经尝试了这个名声> 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)
Run Code Online (Sandbox Code Playgroud)

解释报告在所有情况下都是相同的,但如果查询在Users表中找到更多匹配的行,那么它自然必须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!
Run Code Online (Sandbox Code Playgroud)

这是解释报告:

          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
Run Code Online (Sandbox Code Playgroud)

  • @Chris Padfield:然后你需要用EXPLAIN进行分析并可能创建一个索引. (2认同)