优化查询(2个简单的左连接)

时间:2011-04-20 20:24:04

标签: mysql join

SELECT fcat.id,fcat.title,fcat.description, 
count(DISTINCT ftopic.id) as number_topics, 
count(DISTINCT fpost.id) as number_posts FROM fcat 
LEFT JOIN ftopic ON fcat.id=ftopic.cat_id 
LEFT JOIN fpost ON ftopic.id=fpost.topic_id 
GROUP BY fcat.id
ORDER BY fcat.ord
LIMIT 100;

ftopic_cat_id索引,fpost.topic_id,fcat.ord

说明:

id      select_type     table   type    possible_keys       key         key_len     ref             rows    Extra
1       SIMPLE          fcat    ALL     PRIMARY             NULL        NULL        NULL            11      Using temporary; Using filesort
1       SIMPLE          ftopic  ref     PRIMARY,cat_id_2    cat_id_2    4           bloki.fcat.id   72   
1       SIMPLE          fpost   ref     topic_id_2          topic_id_2  4           bloki.ftopic.id 245 

fcat - 11行, ftopic - 1106行, fpost - 363000行

查询需要4,2秒

TABLES:

CREATE TABLE IF NOT EXISTS `fcat` (
  `id` int(11) NOT NULL auto_increment,
  `title` varchar(250) collate utf8_unicode_ci NOT NULL,
  `description` varchar(250) collate utf8_unicode_ci NOT NULL,
  `created` datetime NOT NULL,
  `visible` tinyint(4) NOT NULL default '1',
  `ord` int(11) NOT NULL,
  PRIMARY KEY  (`id`),
  KEY `ord` (`ord`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=12 ;

CREATE TABLE IF NOT EXISTS `ftopic` (
  `id` int(11) NOT NULL auto_increment,
  `cat_id` int(11) NOT NULL,
  `title` varchar(100) collate utf8_unicode_ci NOT NULL,
  `created` datetime NOT NULL,
  `updated` timestamp NOT NULL default CURRENT_TIMESTAMP,
  `lastname` varchar(200) collate utf8_unicode_ci NOT NULL,
  `visible` tinyint(4) NOT NULL default '1',
  `closed` tinyint(4) NOT NULL default '0',
  `views` int(11) NOT NULL default '1',
  PRIMARY KEY  (`id`),
  KEY `cat_id_2` (`cat_id`,`updated`,`visible`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1116 ;

CREATE TABLE IF NOT EXISTS `fpost` (
  `id` int(11) NOT NULL auto_increment,
  `topic_id` int(11) NOT NULL,
  `pet_id` int(11) NOT NULL,
  `content` text collate utf8_unicode_ci NOT NULL,
  `imageName` varchar(300) collate utf8_unicode_ci NOT NULL,
  `created` datetime NOT NULL,
  `reply_id` int(11) NOT NULL,
  `visible` tinyint(4) NOT NULL default '1',
  `md5` varchar(100) collate utf8_unicode_ci NOT NULL,
  PRIMARY KEY  (`id`),
  UNIQUE KEY `md5` (`md5`),
  KEY `topic_id_2` (`topic_id`,`created`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=390971 ;

谢谢, 村庄

2 个答案:

答案 0 :(得分:0)

您需要同时使用fcat.id, fcat.ord

创建密钥

答案 1 :(得分:0)

大胆重写

此代码在功能上并不相同,但是......

因为你想了解distinct ftopic.id和fpost.id我会大胆并建议两个INNER JOIN而不是LEFT JOIN。 然后因为两个id是自动递增的,它们将不再重复,所以你可以删除distinct

SELECT 
  fcat.id
  , fcat.title
  , fcat.description
  , count(ftopic.id) as number_topics
  , count(fpost.id) as number_posts 
FROM fcat 
INNER JOIN ftopic ON fcat.id = ftopic.cat_id 
INNER JOIN fpost ON ftopic.id = fpost.topic_id 
GROUP BY fcat.id
ORDER BY fcat.ord
LIMIT 100;

这取决于您的数据,如果这是您正在寻找的,但我猜它会更快。

但是你的所有索引似乎都是有序的。

MySQL不会为小样本使用索引!
请注意,explain列表中的MySQL只有 11行才能考虑fcat。这还不足以让MySQL真正开始担心索引,所以它没有 因为转向小行数的索引会降低速度。

MySQL试图加快速度,因此它选择不使用索引,这让很多人感到困惑,因为我们在索引上受过如此严格的训练。小样本量没有给出好的解释!

增加测试数据的大小,以便MySQL有更多的行需要考虑,你应该开始看到正在使用的索引。

关于force index 的常见误解 Force index 强制MySQL使用索引 它暗示MySQL使用不同的索引,它可以自然地使用,它通过在表扫描上设置非常高的成本来推动MySQL使用索引。登记/> (在您的情况下,MySQL 使用表扫描,因此force index无效)

MySQL(与地球上大多数其他DBMS相同)都强烈要求使用索引,因此如果它没有(使用任何),那是因为根本不使用索引会更快。

MySQL如何知道使用哪个索引
查询优化器使用的参数之一是索引的存储基数 随着时间的推移,这些值会发生变化......但是研究这个表需要时间,所以除非你告诉它,否则MySQL不会这样做 另一个影响索引选择的参数是MySQL在执行查询时预计会遇到的预测磁盘搜索时间。

提高索引使用率的提示

  1. ANALYZE TABLE将指示MySQL重新评估索引并更新其密钥分发(基数)。 (考虑每天/每周在cron作业中运行)
  2. SHOW INDEX FROM table将显示密钥分配。
  3. MyISAM表和索引随着时间的推移而分段。使用OPTIMIZE TABLE对表进行碎片整理并重新创建索引。
  4. FORCE/USE/IGNORE INDEX限制MySQL的查询优化器执行查询的选项。只考虑复杂的查询。
  5. 定期干预指数的影响。今天加速查询的强制索引可能会在明天降低速度,因为基础数据已经发生变化。