【发布时间】:2016-09-04 03:56:44
【问题描述】:
这是我要启动的查询:
SELECT c.creative_id, c.creative_title, c.creative_image_name, c.gravity, c.ad_strength
FROM creatives AS c
INNER JOIN term_relationships AS tr ON c.creative_id = tr.creative_id
WHERE tr.term_id
IN ( 14, 1, 50, 76, 104 )
GROUP BY c.creative_id
HAVING COUNT(tr.term_id ) =5
ORDER BY c.gravity ASC
LIMIT 30;
这是EXPLAIN 查询的输出:
这是creatives 表结构:
CREATE TABLE `creatives` (
`creative_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`scraper_id` bigint(20) unsigned DEFAULT NULL,
`creative_title` varchar(255) NOT NULL,
`creative_image_name` varchar(255) DEFAULT NULL,
`image_attrib` varchar(12) DEFAULT NULL,
`original_image_name` varchar(255) DEFAULT NULL,
`creative_subtext` varchar(255) DEFAULT NULL,
`dest_url` varchar(2083) NOT NULL,
`lp_url` varchar(2083) NOT NULL,
`lp_image_name` varchar(255) DEFAULT NULL,
`lp_image_flag` tinyint(1) unsigned NOT NULL DEFAULT '0',
`creative_first_seen` date NOT NULL,
`creative_last_seen` date NOT NULL,
`daily_ad_count` int(5) unsigned NOT NULL,
`ad_strength` int(11) unsigned NOT NULL,
`prev_ad_strength` int(11) unsigned DEFAULT NULL,
`gravity` int(11) unsigned DEFAULT NULL,
PRIMARY KEY (`creative_id`),
KEY `gravity` (`gravity`)
) ENGINE=InnoDB AUTO_INCREMENT=173037591 DEFAULT CHARSET=utf8
我担心Using temporary; using filesort 在另一列上同时启动GROUP BY 和ORDER BY。如果我删除ORDER BY,临时文件和文件排序就会消失,查询运行得非常快。
什么我不明白,为什么mysql需要临时表,为什么不能先where filter + sort by c.gravity,然后按结果表分组并根据HAVING子句过滤。过滤后的表格将按c.gravity正确排序,因为在分组和过滤后重力值保持不变。
我尝试了什么:
选择没有
ORDER BY的所有内容,包装到子查询中并再次加入creatives表 - 结果相同,使用临时、文件排序和慢速尝试添加
FORCE USE INDEX FOR ORDER BY (gravity)并没有改变任何东西。EXPLAIN和执行时间保持不变。
更新:@Rick 已经回答了这个问题,并且使用他的相关子查询而不使用GROUP BY 确实要快得多。我在这里为查询添加EXPLAIN 输出:
以及SHOW CREATE TABLE term_relationships 的输出以及新创建的索引:
还有一个要问@Rick 的问题:为什么我们需要带有c3 的外部查询?似乎只是单独加入creatives,只是为了从其他列中获取值并按重力对记录进行排序。但是,它们已经使用内部查询进行了排序,我们可以轻松地在 c1 中添加缺失的列:
SELECT c1.creative_id,c1.creative_title,c1.creative_image_name,c1.gravity, c1.ad_strength
FROM creatives AS c1
WHERE
( SELECT COUNT(*)
FROM term_relationships
WHERE c1.creative_id = creative_id
AND term_id IN ( 14, 1, 50, 76, 104 )
) = 5
ORDER BY c1.gravity ASC
LIMIT 30;
我的理解正确还是我在您的查询中遗漏了什么?
【问题讨论】:
-
(关于
c3)没有c3,只有creative_id被拖到tmp 表中。使用c3,可以拖拽很多列。如果LIMIT(30) 与LIMITing之前的行数有很大差异,则可能存在明显的性能差异。
标签: mysql indexing query-optimization query-performance