【发布时间】:2015-06-04 06:58:21
【问题描述】:
我正在尝试按列分组,然后在 ORDER BY 另一列之后。它们都是索引,但除非我强迫它们,否则它们不会被使用。
这是我的查询:
SELECT a.*
FROM
(
SELECT
id
FROM
articles temp FORCE INDEX (id_pub_date)
INNER JOIN
sources_articles sa FORCE INDEX (source_id) ON temp.id = sa.article_id
WHERE
sa.source_id IN (10,11,12,13,15,19,33,37,40,41,46)
GROUP BY id
ORDER BY pub_date DESC
LIMIT 0,10
) id_array
INNER JOIN
articles a ON a.id = id_array.id
这是我的数据库架构: article 大约有 160,000 行,sources_articles 大约有 200,000 行。
DROP TABLE IF EXISTS `articles`;
CREATE TABLE `articles` (
`id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`title` char(255) COLLATE utf8_unicode_ci NOT NULL,
`img_url` char(255) CHARACTER SET latin1 NOT NULL,
`vid_url` varchar(511) COLLATE utf8_unicode_ci NOT NULL,
`vid_content_type` varchar(31) COLLATE utf8_unicode_ci NOT NULL,
`vid_thumb_url` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`description` text COLLATE utf8_unicode_ci NOT NULL,
`url` varchar(511) CHARACTER SET latin1 NOT NULL,
`date` int(10) unsigned NOT NULL,
`pub_date` int(10) unsigned NOT NULL,
`comment_url` char(255) CHARACTER SET latin1 NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `url` (`url`),
KEY `id_pub_date` (`id`,`pub_date`),
KEY `pub_date` (`pub_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
DROP TABLE IF EXISTS `sources`;
CREATE TABLE `sources` (
`s_id` tinyint(3) unsigned NOT NULL AUTO_INCREMENT,
`s_name` char(255) CHARACTER SET latin1 NOT NULL,
`s_short_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`s_slug` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`s_category` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`s_default` tinyint(1) NOT NULL,
`s_active` tinyint(1) NOT NULL,
PRIMARY KEY (`s_id`),
KEY `s_slug` (`s_slug`),
KEY `s_default` (`s_default`,`s_active`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='List of sources';
DROP TABLE IF EXISTS `sources_articles`;
CREATE TABLE `sources_articles` (
`sa_id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
`source_id` tinyint(3) unsigned NOT NULL,
`article_id` mediumint(8) unsigned NOT NULL,
PRIMARY KEY (`sa_id`),
KEY `source_id` (`source_id`),
KEY `article_id` (`article_id`),
CONSTRAINT `sources_articles_ibfk_1` FOREIGN KEY (`source_id`) REFERENCES `sources` (`s_id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `sources_articles_ibfk_2` FOREIGN KEY (`article_id`) REFERENCES `articles` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
查询运行大约需要 600 毫秒。我正在使用由 id 和 pub_date 列组成的索引。
如果我强制 id 作为 temp 的索引,那么 GROUP BY 和 ORDER BY id,查询大约需要 3ms。
如果我强制 pub_date 作为索引,然后 GROUP BY 和 ORDER BY pub_date,则相同。 p>
我在想,如果我在分组时强制索引 id,并在排序时强制 pub_date 作为索引,它会运行得更快。据我了解,这在 mysql 上是不可能的。
当然,我可能看错了整个事情。
非常感谢任何提示或帮助 :)
编辑:
通过这个新的、更新的查询能够减少几百毫秒:
SELECT DISTINCT a.*
FROM
(
SELECT
id
FROM
articles temp FORCE INDEX (id_pub_date)
INNER JOIN
sources_articles sa FORCE INDEX (source_id_article_id) ON temp.id = sa.article_id
WHERE
sa.source_id IN (10,11,12,13,15,19,33,37,40,41,46)
ORDER BY pub_date DESC
LIMIT 0,10
) id_array
INNER JOIN
articles a ON a.id = id_array.id
【问题讨论】:
-
你不能。 MySQL 将在一个语句空间内只使用一个索引(实际上,它可以只使用一个索引)
-
删除 sa_id。删除 sa.source_id 和 sa.article_id 上的索引。而是将这两列用作您的 PK。如果您必须保留您的代理 PK,请至少将自然键设为 UNIQUE。
-
另外,删除超级查询。选择 id 时只需选择 a.columns。毕竟是PK!!
-
@Strawberry - 超级查询的好处是子查询的 tmp 表只需要保留“id”,而不是整行。请注意,子查询有一个
LIMIT,并且子查询可能是“使用索引”,这两者都放大了不拖着“a.*”的好处。
标签: mysql sql database performance join