2008-10-09 73 views
15

我一直在分析我正在处理的应用程序中的一些查询,并且遇到了一个查询,该查询检索的行数多于必要的数量,结果集在应用程序代码中被裁减。左连接优于内连接?

将LEFT JOIN更改为INNER JOIN将结果集修剪为所需内容,并且可能还会更高效(因为选择的行数更少)。事实上,LEFT JOINED的查询表现超过INNER JOIN'ED,花了一半时间完成。

LEFT JOIN:(127点总的行,查询花费0.0011秒)

INNER JOIN:(10点总的行,查询花费0.0024秒)

(我跑的查询多次,并且这些是平均数) 。

运行EXPLAIN上都显示没有任何解释的性能差异:

对于INNER JOIN:

id select_type  table type possible_keys key  key_len  ref  rows  Extra 
1 SIMPLE contacts  index  NULL  name  302  NULL   235 Using where 
1 SIMPLE lists   eq_ref  PRIMARY  PRIMARY  4 contacts.list_id  1 
1 SIMPLE lists_to_users eq_ref  PRIMARY  PRIMARY  8 lists.id,const 1  
1 SIMPLE tags   eq_ref  PRIMARY  PRIMARY  4 lists_to_users.tag_id 1  
1 SIMPLE users   eq_ref  email_2  email_2  302  contacts.email 1 Using where 

对于LEFT JOIN:

id select_type  table type possible_keys key  key_len  ref  rows Extra 
1 SIMPLE   contacts index  NULL  name  302  NULL 235  Using where 
1 SIMPLE  lists  eq_ref  PRIMARY  PRIMARY  4 contacts.list_id 1  
1 SIMPLE lists_to_users eq_ref  PRIMARY  PRIMARY  8 lists.id,const 1  
1 SIMPLE   tags  eq_ref  PRIMARY  PRIMARY  4 lists_to_users.tag_id 1  
1 SIMPLE  users  eq_ref  email_2  email_2  302  contacts.email 1 

和查询本身:

SELECT `contacts`.*, `lists`.`name` AS `group`, `lists`.`id` AS `group_id`, `lists`.`shared_yn`, `tags`.`name` AS `context`, `tags`.`id` AS `context_id`, `tags`.`color` AS `context_color`, `users`.`id` AS `user_id`, `users`.`avatar` 
FROM `contacts` 
LEFT JOIN `lists` ON lists.id=contacts.list_id 
LEFT JOIN `lists_to_users` ON lists_to_users.list_id=lists.id AND lists_to_users.user_id='1' AND lists_to_users.creator='1' 
LEFT JOIN `tags` ON tags.id=lists_to_users.tag_id 
INNER JOIN `users` ON users.email=contacts.email 
WHERE (contacts.user_id='1') 
ORDER BY `contacts`.`name` ASC 

(我所说的子句是“用户”表上的最后一个INNER JOIN)

查询在MySQL 5.1数据库上运行,如果它有所不同。

有没有人有一个线索,为什么在这种情况下,LEFT JOIN的查询优于INNER JOIN?

更新:由于Tomalak的建议,我使用的小表使INNER JOIN更复杂,我创建了一个包含一些模拟数据的测试数据库。 “用户”表格为5000行,联系人表格为〜500,000行。结果是一样的(时间也没有改变,当你认为表格现在更大时,这是令人惊讶的)。

我也在联系人表上运行ANALYZE和OPTIMIZE。没有做出任何明显的区别。

+0

您是否尝试先放置内连接? – 2008-10-09 06:10:02

+0

我有,它确实加快了20%的查询速度,但仍然比左加入速度更慢 – 2008-10-09 06:14:43

+0

尝试按顺序构建每个查询(加入一个表,测量,加入下一个等等)也许这可以帮助您确定慢速操作。 – Tomalak 2008-10-09 06:21:48

回答

6

这可能是由于INNER JOIN必须检查两个表中的每一行来查看列值(电子邮件)是否匹配。无论如何,LEFT JOIN都会从一个表中返回。如果它被索引,那么它也会知道该怎么做。

4

表基数对查询优化器有影响。我猜想,小型表格会让内连接成为更复杂的操作。只要有更多的记录比DB服务器愿意保留在内存中,内部联接可能会开始超过左侧联接。

2

即时通讯您正在陷入称为过早优化的陷阱。查询优化器是疯狂变幻莫测的事情。我的建议是继续前进,直到你确定某个连接是否有问题。

-3

LEFT JOIN返回的行比INNER JOIN多,因为这两个不同。
如果LEFT JOIN在查找的表中找不到相关条目,它将返回表的NULL。
但是,如果INNER JOIN没有找到相关条目,它根本不会返回整个行。

但你的问题,你有query_cache启用? 尝试用

SELECT SQL_NO_CACHE `contacts`.*, ... 

除此之外,运行查询,我会填充表与数据越多,跑

ANALYZE TABLE t1, t2; 
OPTIMIZE TABLE t1, t2; 

看看会发生什么。

12

如果你认为LEFT JOIN的实现是INNER JOIN +更多的工作,那么这个结果是令人困惑的。如果INNER JOIN的实现是(LEFT JOIN +过滤)会怎么样?啊,现在很清楚。

在查询计划中,唯一不同的是:users ... extra:using where。这意味着过滤。在带有内部联接的查询中有一个额外的筛选步骤


这是一种不同于通常在where子句中使用的过滤。在A上创建索引以支持此过滤操作很简单。

SELECT * 
FROM A 
WHERE A.ID = 3 

考虑这个查询:

SELECT * 
FROM A 
    LEFT JOIN B 
    ON A.ID = B.ID 
WHERE B.ID is not null 

该查询等效于内连接。 B上没有索引会帮助进行过滤操作。其原因是,where子句是在B.

述明的加入结果的条件,而不是条件
0

试试这个:

SELECT `contacts`.*, `lists`.`name` AS `group`, `lists`.`id` AS `group_id`, `lists`.`shared_yn`, `tags`.`name` AS `context`, `tags`.`id` AS `context_id`, `tags`.`color` AS `context_color`, `users`.`id` AS `user_id`, `users`.`avatar` 
FROM `contacts` 
INNER JOIN `users` ON contacts.user_id='1' AND users.email=contacts.email 
LEFT JOIN `lists` ON lists.id=contacts.list_id 
LEFT JOIN `lists_to_users` ON lists_to_users.user_id='1' AND lists_to_users.creator='1' AND lists_to_users.list_id=lists.id 
LEFT JOIN `tags` ON tags.id=lists_to_users.tag_id 
ORDER BY `contacts`.`name` ASC 

这应该给你额外的性能,因为:

  • 您在任何“左”或“右”连接出现之前放置所有内连接。在应用后续外连接之前,这会过滤掉一些记录。
  • “AND”运算符的短路(“AND”的顺序很重要)。如果列和文字之间的特点比较是错误的,它不会执行所需的表扫描的表的PK和FKS

之间的特点比较。如果你没有找到任何的性能提升,然后全部更换“COUNT(*)”的列集并进行左/内部测试。这样一来,不管查询,你会只检索1单行1个单柱(伯爵),这样你就可以丢弃返回的字节数是您的查询的缓慢的原因:

SELECT COUNT(*) 
FROM `contacts` 
INNER JOIN `users` ON contacts.user_id='1' AND users.email=contacts.email 
LEFT JOIN `lists` ON lists.id=contacts.list_id 
LEFT JOIN `lists_to_users` ON lists_to_users.user_id='1' AND lists_to_users.creator='1' AND lists_to_users.list_id=lists.id 
LEFT JOIN `tags` ON tags.id=lists_to_users.tag_id 

祝你好运