14

我正在使用 InnoDB。

查询、解释和索引

选择
  故事。*,
  count(comments.id) 作为评论,
  GROUP_CONCAT(
    DISTINCT 分类 2.name SEPARATOR ';'
  ) 作为分类名称,
  GROUP_CONCAT(
    不同的图像.id
    ORDER BY images.position,
    images.id 分隔符';'
  ) 作为 images_id,
  GROUP_CONCAT(
    不同的图像.caption
    ORDER BY images.position,
    images.id 分隔符';'
  ) 作为 images_caption,
  GROUP_CONCAT(
    不同的图像.thumbnail
    ORDER BY images.position,
    images.id 分隔符';'
  ) 作为 images_thumbnail,
  GROUP_CONCAT(
    不同的图像.medium
    ORDER BY images.position,
    images.id 分隔符';'
  ) 作为图像_媒体,
  GROUP_CONCAT(
    不同的图像.大
    ORDER BY images.position,
    images.id 分隔符';'
  ) AS images_large,
  GROUP_CONCAT(
    DISTINCT users.id
    ORDER BY users.id SEPARATOR ';'
  ) 作为作者 ID,
  GROUP_CONCAT(
    DISTINCT users.display_name
    ORDER BY users.id SEPARATOR ';'
  ) 作为作者显示名称,
  GROUP_CONCAT(
    不同的 users.url
    ORDER BY users.id SEPARATOR ';'
  ) 作为作者 URL
从
  故事
  LEFT JOIN 分类
    ON stories.id = 分类.story_id
  LEFT JOIN 分类 AS 分类2
    ON stories.id = classifications2.story_id
  左加入评论
    ON stories.id = comments.story_id
  左连接 image_story
    ON stories.id = image_story.story_id
      左连接图像
        ON images.id = image_story.`image_id`
  左连接 author_story
    ON stories.id = author_story.story_id
      左加入用户
        ON users.id = author_story.author_id
 WHERE 分类。`name` LIKE 'Home:Top%'
   AND stories.status = 1
GROUP BY stories.id
按分类排序。`名称`,分类。`位置`

+----+-------------+------------------+--------+-- -------------+---------+---------+--------------- ---------+--------+------------------------------- ---------------+
| 编号 | 选择类型 | 表| 类型 | 可能的键 | 关键 | key_len | 参考 | 行 | 额外 |
+----+-------------+------------------+--------+-- -------------+---------+---------+--------------- ---------+--------+------------------------------- ---------------+
| 1 | 简单 | 故事 | 参考 | 状态 | 状态 | 1 | 常量 | 434792 | 使用哪里;使用临时的;使用文件排序 |
| 1 | 简单 | 分类 | 参考 | 故事ID | 故事ID | 4 | 故事.id | 1 | 使用位置 |
| 1 | 简单 | 分类2 | 参考 | 故事ID | 故事ID | 4 | 故事.id | 1 | 使用位置 |
| 1 | 简单 | 评论 | 参考 | 故事ID | 故事ID | 8 | 故事.id | 6 | 使用哪里;使用索引 |
| 1 | 简单 | 图片故事 | 参考 | 故事ID | 故事ID | 4 | 故事.id | 1 | 空 |
| 1 | 简单 | 图片 | eq_ref | 初级 | 初级 | 4 | image_story.image_id | 1 | 空 |
| 1 | 简单 | 作者故事 | 参考 | 故事ID | 故事ID | 4 | 故事.id | 1 | 使用位置 |
| 1 | 简单 | 用户 | eq_ref | 初级 | 初级 | 4 | author_story.author_id | 1 | 使用位置 |
+----+-------------+------------------+--------+-- -------------+---------+---------+--------------- ---------+--------+------------------------------- ---------------+

+-----------------+------------+-------------+---- ----------+-------------+------------+------------- +---------+--------+------+------------+
| 表 | 非唯一 | 键名 | Seq_in_index | 列名 | 整理 | 基数| 子部分 | 包装 | 空 | 索引类型 |
+-----------------+------------+-------------+---- ----------+-------------+------------+------------- +---------+--------+------+------------+
| 故事 | 0 | 初级 | 1 | 编号 | 一个 | 869584 | 空 | 空 | | BTREE |
| 故事 | 1 | created_at | 1 | created_at | 一个 | 434792 | 空 | 空 | | BTREE |
| 故事 | 1 | 来源 | 1 | 来源 | 一个 | 2 | 空 | 空 | 是 | BTREE |
| 故事 | 1 | source_id | 1 | source_id | 一个 | 869584 | 空 | 空 | 是 | BTREE |
| 故事 | 1 | 类型 | 1 | 类型 | 一个 | 2 | 空 | 空 | | BTREE |
| 故事 | 1 | 状态 | 1 | 状态 | 一个 | 2 | 空 | 空 | | BTREE |
| 故事 | 1 | 类型状态 | 1 | 类型 | 一个 | 2 | 空 | 空 | | BTREE |
| 故事 | 1 | 类型状态 | 2 | 状态 | 一个 | 2 | 空 | 空 | | BTREE |
| 分类 | 0 | 初级 | 1 | 编号 | 一个 | 207 | 空 | 空 | | BTREE |
| 分类 | 1 | 故事ID | 1 | 故事ID | 一个 | 207 | 空 | 空 | | BTREE |
| 分类 | 1 | 姓名 | 1 | 姓名 | 一个 | 103 | 空 | 空 | | BTREE |
| 分类 | 1 | 姓名 | 2 | 职位 | 一个 | 207 | 空 | 空 | 是 | BTREE |
| 评论 | 0 | 初级 | 1 | 编号 | 一个 | 239336 | 空 | 空 | | BTREE |
| 评论 | 1 | 状态 | 1 | 状态 | 一个 | 2 | 空 | 空 | | BTREE |
| 评论 | 1 | 日期 | 1 | 日期 | 一个 | 239336 | 空 | 空 | | BTREE |
| 评论 | 1 | 故事ID | 1 | 故事ID | 一个 | 39889 | 空 | 空 | | BTREE |
+-----------------+------------+-------------+---- ----------+-------------+------------+------------- +---------+--------+------+------------+

查询时间

平均0.035 seconds需要运行。

如果我只删除,平均GROUP BY时间会下降到0.007

如果我只删除stories.status=1过滤器,时间平均下降到0.025。这似乎可以很容易地优化。

如果我只删除LIKE过滤器和ORDER BY子句,0.006平均时间会下降。

更新 1:2013-04-13

我的理解通过答案得到了多方面的改善。

我添加了索引author_storyimages_story这似乎改进了0.025几秒钟的查询,但出于某种奇怪的原因,该EXPLAIN计划看起来好多了。此时删除ORDER BY删除查询到0.015秒并删除两者ORDER BY并将GROUP BY查询性能提高到0.006. 我是现在要关注的两件事吗?如果需要,我可能会ORDER BY进入应用程序逻辑。

以下是修改后EXPLAININDEXES

+----+-------------+------------------+--------+-- -------------------------------+----------+-------- --+--------------+------+-------------- -------------------------------------------------------+
| 编号 | 选择类型 | 表| 类型 | 可能的键 | 关键 | key_len | 参考 | 行 | 额外 |
+----+-------------+------------------+--------+-- -------------------------------+----------+-------- --+--------------+------+-------------- -------------------------------------------------------+
| 1 | 简单 | 分类 | 范围 | story_id,名称 | 姓名 | 102 | 空 | 14 | 使用索引条件;使用临时的;使用文件排序 |
| 1 | 简单 | 故事 | eq_ref | 初级,状态 | 初级 | 4 | 分类.story_id | 1 | 使用位置 |
| 1 | 简单 | 分类2 | 参考 | 故事ID | 故事ID | 4 | 故事.id | 1 | 使用位置 |
| 1 | 简单 | 作者故事 | 参考 | author_id,story_id,author_story | 故事ID | 4 | 故事.id | 1 | 使用索引条件 |
| 1 | 简单 | 用户 | eq_ref | 初级 | 初级 | 4 | author_story.author_id | 1 | 使用位置 |
| 1 | 简单 | 评论 | 参考 | 故事ID | 故事ID | 8 | 故事.id | 8 | 使用哪里;使用索引 |
| 1 | 简单 | 图片故事 | 参考 | story_id,story_id_2 | 故事ID | 4 | 故事.id | 1 | 空 |
| 1 | 简单 | 图片 | eq_ref | PRIMARY,position_id | 初级 | 4 | image_story.image_id | 1 | 空 |
+----+-------------+------------------+--------+-- -------------------------------+----------+-------- --+--------------+------+-------------- -------------------------------------------------------+


+-----------------+------------+------------------ --+----------------+--------------+------------+------ --------+----------+--------+------+------------+-- --------+---------------+
| 表 | 非唯一 | 键名 | Seq_in_index | 列名 | 整理 | 基数| 子部分 | 包装 | 空 | 索引类型 | 评论 | 索引评论 |
+-----------------+------------+------------------ --+----------------+--------------+------------+------ --------+----------+--------+------+------------+-- --------+---------------+
| 作者故事 | 0 | 初级 | 1 | 编号 | 一个 | 220116 | 空 | 空 | | BTREE | | |
| 作者故事 | 0 | 故事作者 | 1 | 故事ID | 一个 | 220116 | 空 | 空 | | BTREE | | |
| 作者故事 | 0 | 故事作者 | 2 | 作者_id | 一个 | 220116 | 空 | 空 | | BTREE | | |
| 作者故事 | 1 | 作者_id | 1 | 作者_id | 一个 | 第2179章 空 | 空 | | BTREE | | |
| 作者故事 | 1 | 故事ID | 1 | 故事ID | 一个 | 220116 | 空 | 空 | | BTREE | | |
| 图片故事 | 0 | 初级 | 1 | 编号 | 一个 | 148902 | 空 | 空 | | BTREE | | |
| 图片故事 | 0 | 故事图片 | 1 | 故事ID | 一个 | 148902 | 空 | 空 | | BTREE | | |
| 图片故事 | 0 | 故事图片 | 2 | image_id | 一个 | 148902 | 空 | 空 | | BTREE | | |
| 图片故事 | 1 | 故事ID | 1 | 故事ID | 一个 | 148902 | 空 | 空 | | BTREE | | |
| 图片故事 | 1 | image_id | 1 | image_id | 一个 | 148902 | 空 | 空 | | BTREE | | |
| 分类 | 0 | 初级 | 1 | 编号 | 一个 | 257 | 空 | 空 | | BTREE | | |
| 分类 | 1 | 故事ID | 1 | 故事ID | 一个 | 257 | 空 | 空 | | BTREE | | |
| 分类 | 1 | 姓名 | 1 | 姓名 | 一个 | 128 | 空 | 空 | | BTREE | | |
| 分类 | 1 | 姓名 | 2 | 职位 | 一个 | 257 | 空 | 空 | 是 | BTREE | | |
| 故事 | 0 | 初级 | 1 | 编号 | 一个 | 962570 | 空 | 空 | | BTREE | | |
| 故事 | 1 | created_at | 1 | created_at | 一个 | 481285 | 空 | 空 | | BTREE | | |
| 故事 | 1 | 来源 | 1 | 来源 | 一个 | 4 | 空 | 空 | 是 | BTREE | | |
| 故事 | 1 | source_id | 1 | source_id | 一个 | 962570 | 空 | 空 | 是 | BTREE | | |
| 故事 | 1 | 类型 | 1 | 类型 | 一个 | 2 | 空 | 空 | | BTREE | | |
| 故事 | 1 | 状态 | 1 | 状态 | 一个 | 4 | 空 | 空 | | BTREE | | |
| 故事 | 1 | 类型状态 | 1 | 类型 | 一个 | 2 | 空 | 空 | | BTREE | | |
| 故事 | 1 | 类型状态 | 2 | 状态 | 一个 | 6 | 空 | 空 | | BTREE | | |
| 评论 | 0 | 初级 | 1 | 编号 | 一个 | 232559 | 空 | 空 | | BTREE | | |
| 评论 | 1 | 状态 | 1 | 状态 | 一个 | 6 | 空 | 空 | | BTREE | | |
| 评论 | 1 | 日期 | 1 | 日期 | 一个 | 232559 | 空 | 空 | | BTREE | | |
| 评论 | 1 | 故事ID | 1 | 故事ID | 一个 | 29069 | 空 | 空 | | BTREE | | |
| 图片 | 0 | 初级 | 1 | 编号 | 一个 | 147206 | 空 | 空 | | BTREE | | |
| 图片 | 0 | source_id | 1 | source_id | 一个 | 147206 | 空 | 空 | 是 | BTREE | | |
| 图片 | 1 | 职位 | 1 | 职位 | 一个 | 4 | 空 | 空 | | BTREE | | |
| 图片 | 1 | position_id | 1 | 编号 | 一个 | 147206 | 空 | 空 | | BTREE | | |
| 图片 | 1 | position_id | 2 | 职位 | 一个 | 147206 | 空 | 空 | | BTREE | | |
| 用户 | 0 | 初级 | 1 | 编号 | 一个 | 981 | 空 | 空 | | BTREE | | |
| 用户 | 0 | users_email_unique | 1 | 电子邮件 | 一个 | 981 | 空 | 空 | | BTREE | | |
+-----------------+------------+------------------ --+----------------+--------------+------------+------ --------+----------+--------+------+------------+-- --------+---------------+



选择
故事。*,
count(comments.id) 作为评论,
GROUP_CONCAT(DISTINCT users.id ORDER BY users.id SEPARATOR ';') AS authors_id,
GROUP_CONCAT(DISTINCT users.display_name ORDER BY users.id SEPARATOR ';') AS authors_display_name,
GROUP_CONCAT(DISTINCT users.url ORDER BY users.id SEPARATOR ';') AS authors_url,
GROUP_CONCAT(DISTINCT classifications2.name SEPARATOR ';') AS classifications_name,
GROUP_CONCAT(DISTINCT images.id ORDER BY images.position,images.id SEPARATOR ';') AS images_id,
GROUP_CONCAT(DISTINCT images.caption ORDER BY images.position,images.id SEPARATOR ';') AS images_caption,
GROUP_CONCAT(DISTINCT images.thumbnail ORDER BY images.position,images.id SEPARATOR ';') AS images_thumbnail,
GROUP_CONCAT(DISTINCT images.medium ORDER BY images.position,images.id SEPARATOR ';') AS images_medium,
GROUP_CONCAT(DISTINCT images.large ORDER BY images.position,images.id SEPARATOR ';') AS images_large
从
  分类
  INNER JOIN 故事
    ON stories.id = 分类.story_id
  LEFT JOIN 分类 AS 分类2
    ON stories.id = classifications2.story_id
  左加入评论
    ON stories.id = comments.story_id
  左连接 image_story
    ON stories.id = image_story.story_id
  左连接图像
    ON images.id = image_story.`image_id`
 INNER JOIN author_story
    ON stories.id = author_story.story_id
 INNER JOIN 用户
    ON users.id = author_story.author_id
 WHERE 分类。`name` LIKE 'Home:Top%'
   AND stories.status = 1
GROUP BY stories.id
按空排序

更新 2:2013-04-14

我注意到另一件事。如果我不选择stories.content(LONGTEXT) 和stories.content_html(LONGTEXT),则查询会从0.015几秒下降到0.006几秒。现在我正在考虑是否可以不用它们contentcontent_html或者用其他东西代替它们。

我在上面的 2013-04-13 更新中更新了查询、索引和解释,而不是在这个更新中重新发布,因为它们是次要的和增量的。该查询仍在使用filesort. 我无法摆脱,GROUP BY但已经摆脱了ORDER BY

更新 3:2013-04-16

根据要求,我从两者中删除了 stories_id INDEXES image_storyauthor_story因为它们是多余的。结果是解释的输出只发生了变化,表明发生了possible_keys变化。不幸的是,它仍然没有显示Using Index优化。

也更改LONGTEXTTEXT现在正在获取LEFT(stories.content, 500),而不是stories.content在查询执行时间上产生了非常显着的差异。

+----+-------------+------------------+--------+-- ---------------+--------------+-------- --+--------------+------+-------------- -------------------------------------------------- ------+
| 编号 | 选择类型 | 表| 类型 | 可能的键 | 关键 | key_len | 参考 | 行 | 额外 |
+----+-------------+------------------+--------+-- ---------------+--------------+-------- --+--------------+------+-------------- -------------------------------------------------- ------+
| 1 | 简单 | 分类 | 参考 | 故事ID,姓名,姓名位置| 姓名 | 102 | 常量 | 10 | 使用索引条件;使用哪里;使用临时的;使用文件排序 |
| 1 | 简单 | 故事 | eq_ref | 初级,状态 | 初级 | 4 | 分类.story_id | 1 | 使用位置 |
| 1 | 简单 | 分类2 | 参考 | 故事ID | 故事ID | 4 | 故事.id | 1 | 使用位置 |
| 1 | 简单 | 作者故事 | 参考 | 故事作者 | 故事作者 | 4 | 故事.id | 1 | 使用哪里;使用索引 |
| 1 | 简单 | 用户 | eq_ref | 初级 | 初级 | 4 | author_story.author_id | 1 | 使用位置 |
| 1 | 简单 | 评论 | 参考 | 故事ID | 故事ID | 8 | 故事.id | 8 | 使用哪里;使用索引 |
| 1 | 简单 | 图片故事 | 参考 | 故事图片 | 故事图片 | 4 | 故事.id | 1 | 使用索引 |
| 1 | 简单 | 图片 | eq_ref | PRIMARY,position_id | 初级 | 4 | image_story.image_id | 1 | 空 |
+----+-------------+------------------+--------+-- ---------------+--------------+-------- --+--------------+------+-------------- -------------------------------------------------- ------+

innodb_buffer_pool_size
134217728

TABLE_NAME INDEX_LENGTH
image_story 10010624
image_story 4556800
图片故事 0

TABLE_NAME INDEX_NAMES 大小
黎明/image_story story_image 13921
4

6 回答 6

9

我可以立即看到两个优化机会:

将 OUTER JOIN 更改为 INNER JOIN

您的查询当前正在扫描 434792 个故事,并且您应该能够更好地缩小范围,假设不是每个故事都有一个与“Home:Top%”匹配的分类。最好使用索引来查找您要查找的分类,然后查找匹配的故事。

但是您使用LEFT OUTER JOIN的是分类,这意味着所有故事都将被扫描,无论它们是否具有匹配的分类。然后,您通过在WHERE子句中对分类设置条件来打败它,有效地强制要求有一个与您的模式匹配的分类LIKE。所以它不再是外连接,而是内连接。

如果您将分类表放在首位,并使其成为内部连接,优化器将使用它来将搜索范围缩小到具有匹配分类的故事。

. . . 
FROM
  classifications
  INNER JOIN stories
    ON stories.id = classifications.story_id 
. . .

优化器应该能够确定何时重新排序表是有利的,因此您可能不必更改查询中的顺序。INNER JOIN但是在这种情况下您确实需要使用 an 。

添加复合索引

您的交集表 image_story 和 author_story 没有复合索引。将复合索引添加到多对多关系的交集表中通常是一个很大的优势,这样它们就可以执行连接并获得“使用索引”优化。

ALTER TABLE image_story ADD UNIQUE KEY imst_st_im (story_id, image_id);
ALTER TABLE author_story ADD UNIQUE KEY aust_st_au (story_id, author_id);

重新发表您的评论和更新:

我不确定您是否正确创建了新索引。您的索引转储不显示列,并且根据更新的 EXPLAIN,没有使用新索引,我希望会发生这种情况。使用新索引将导致 EXPLAIN 的额外字段中出现“使用索引”,这应该有助于提高性能。

如您所示,每个表的输出SHOW CREATE TABLE将比索引转储(没有列名)更完整的信息。

创建索引后,您可能必须在每个表上运行一次 ANALYZE TABLE。此外,多次运行查询,以确保索引在缓冲池中。这个表是 InnoDB 还是 MyISAM?

我还注意到在您的 EXPLAIN 输出中,该rows列显示被触摸的行数要少得多。这是一个进步。

你真的需要ORDER BY吗?如果您使用ORDER BY NULL,您应该能够摆脱“使用文件排序”,这可能会提高性能。


重新更新:

您仍然没有从您的image_storyauthor_story表中获得“使用索引”优化。我的一个建议是消除冗余索引:

ALTER TABLE image_story DROP KEY story_id;
ALTER TABLE author_story DROP KEY story_id;

原因是任何可以从 story_id 上的单列索引中受益的查询也可以从 (story_id,image_id) 上的两列索引中受益。消除冗余索引有助于优化器做出更好的决策(以及节省一些空间)。这是pt-duplicate-key-checker等工具背后的理论。

我还会检查以确保您的缓冲池足够大以容纳您的索引。您不希望索引在查询期间分页进出缓冲池。

SHOW VARIABLES LIKE 'innodb_buffer_pool_size'

检查 image_story 表的索引大小:

SELECT TABLE_NAME, INDEX_LENGTH FROM INFORMATION_SCHEMA.TABLES 
WHERE TABLE_NAME = 'image_story';

并将其与这些索引中当前驻留在缓冲池中的数量进行比较:

SELECT TABLE_NAME, GROUP_CONCAT(DISTINCT INDEX_NAME) AS INDEX_NAMES, SUM(DATA_SIZE) AS SIZE 
FROM INFORMATION_SCHEMA.INNODB_BUFFER_PAGE_LRU 
WHERE TABLE_NAME = '`test`.`image_story`' AND INDEX_NAME <> 'PRIMARY'

当然,将上面的 `test` 更改为您的表所属的数据库名称。

该 information_schema 表是 MySQL 5.6 中的新表。我假设您使用的是 MySQL 5.6,因为您的 EXPLAIN 显示“使用索引条件”,这在 MySQL 5.6 中也是新的。

除非我真的需要很长的字符串,否则我根本不使用 LONGTEXT。记住:

  • TEXT 最多可容纳 64KB
  • MEDIUMTEXT 最多可容纳 16MB
  • LONGTEXT 最多可容纳 4GB
于 2013-04-12T03:18:09.203 回答
8

当您使用 MYSQL 时,您可以利用Straight_join

STRAIGHT_JOIN forces the optimizer to join the tables in the order in which they are listed in the FROM clause. You can use this to speed up a query if the optimizer joins the tables in nonoptimal order

还有一个改进范围是过滤表的数据,stories因为您只需要状态为 1的数据

因此,在表单子句中,不要添加整个stories表,而是添加唯一需要的记录,因为您的查询计划显示该表有434792相同的行classification

FROM
   (SELECT 
       * 
   FROM
       STORIES
   WHERE 
       STORIES.status = 1) stories
LEFT JOIN
   (SELECT 
       * 
   FROM
       classifications
   WHERE
       classifications.`name` LIKE 'Home:Top%') classifications
ON stories.id = classifications.story_id 

sort_buffer_size当您显示为order by和时,您还可以增加一个建议group by,但是随着每个会话的缓冲区大小的增加,请小心增加缓冲区大小。

此外,如果可能的话,您可以在您的应用程序中订购您的记录,因为您已经提到删除该order by子句可以提高1/6原始时间的一部分......

编辑

image_story.image_idimage_story表和表添加索引,author_story.story_id因为author_story这些列用于连接

images.position, images.id还必须在您使用它时创建索引。

编辑 16/4

我想你几乎优化了你的查询,看到你更新......

仍然可以改进的一个地方是使用 BillKarwin 提到的适当的数据类型......您可以使用ENUMTINYINT键入状态和其他没有任何增长范围的列,它将帮助您优化查询性能,还您的桌子的存储性能....

希望这可以帮助....

于 2013-04-11T15:45:20.557 回答
6

计算

GROUP_CONCAT(DISTINCT classifications2.name SEPARATOR ';')

可能是最耗时的操作,因为classifications它是一个大表,并且由于所有连接,要使用的行数成倍增加。

因此,我建议使用临时表来获取该信息。此外,为了避免两次计算 LIKE 条件(一次用于临时表,一次用于“真实”查询),我还将为此创建一个临时表。

您的原始查询,在一个非常简化的版本(没有图像和用户表,以便更容易阅读)是:

SELECT
    stories.*,
    count(DISTINCT comments.id) AS comments,
    GROUP_CONCAT(DISTINCT classifications2.name ORDER BY 1 SEPARATOR ';' )
        AS classifications_name
FROM
    stories
    LEFT JOIN classifications
        ON stories.id = classifications.story_id
    LEFT JOIN classifications AS classifications2
        ON stories.id = classifications2.story_id
    LEFT JOIN comments
        ON stories.id = comments.story_id
WHERE
    classifications.`name` LIKE 'Home:Top%'
        AND stories.status = 1
GROUP BY stories.id
    ORDER BY stories.id, classifications.`name`, classifications.`positions`;

我将用以下查询替换它,使用临时表_tmp_filtered_classifications(名称为 LIKE Home:Top%' 的分类 id)和_tmp_classifications_of_story(对于每个故事 id 'contained' in _tmp_filtered_classifications,所有分类名称):

DROP TABLE IF EXISTS `_tmp_filtered_classifications`;

CREATE TEMPORARY TABLE _tmp_filtered_classifications
    SELECT id FROM classifications WHERE name LIKE 'Home:Top%';

DROP TABLE IF EXISTS `_tmp_classifications_of_story`;

CREATE TEMPORARY TABLE _tmp_classifications_of_story ENGINE=MEMORY
    SELECT stories.id AS story_id, classifications2.name
        FROM 
        _tmp_filtered_classifications
        INNER JOIN classifications        
            ON _tmp_filtered_classifications.id=classifications.id
        INNER JOIN stories
            ON stories.id = classifications.story_id
        LEFT JOIN classifications AS classifications2
            ON stories.id = classifications2.story_id
        GROUP BY 1,2;

SELECT
    stories.*,
    count(DISTINCT comments.id) AS comments,
    GROUP_CONCAT(DISTINCT classifications2.name ORDER BY 1 SEPARATOR ';')
        AS classifications_name
FROM
    _tmp_filtered_classifications
    INNER JOIN classifications        
        ON _tmp_filtered_classifications.id=classifications.id
    INNER JOIN stories
        ON stories.id = classifications.story_id
    LEFT JOIN _tmp_classifications_of_story AS classifications2
        ON stories.id = classifications2.story_id
    LEFT JOIN comments
        ON stories.id = comments.story_id
WHERE
    stories.status = 1
GROUP BY stories.id
    ORDER BY stories.id, classifications.`name`, classifications.`positions`;

请注意,我在您的查询中添加了更多“order by”子句,以检查两个查询是否提供相同的结果(使用差异)。我还更改count(comments.id)count(DISTINCT comments.id)否则查询计算的评论数是错误的(同样,因为连接乘以行数)。

于 2013-04-11T14:26:04.450 回答
0

我不知道要实验的数据的所有细节,但我知道您应该首先执行匹配最少数据量的操作,从而为后续操作消除最多数据量。

根据您的整体查询的复杂程度,您可能无法以这种方式重新排序操作。但是,您可以执行两个单独的查询,其中第一个只是消除绝对不需要的数据,然后将其结果提供给第二个查询。其他人建议使用临时表,这是处理这种情况的好方法。

如果您需要对此策略进行任何澄清,请告诉我。

** 更新:当每个操作与其他操作匹配大约相同百分比的数据时使用的类似策略是分别为每个操作计时,然后首先运行使用最少时间的操作。一些搜索操作比其他操作更快,如果所有其他因素都相同,最快的应该是第一个。这样,较慢的搜索操作将使用较少的数据,整体结果将是更高的性能。

于 2013-04-12T00:38:42.543 回答
0

我敢打赌,LIKE条件是您要求的最糟糕的事情。
你确定你必须这样做吗?

4个步骤:

  1. 在表上创建一个IsHomeTop布尔索引列classifications
  2. UPDATE classifications SET IsTopHome = 1 WHERE NAME LIKE 'Home:Top%'
  3. 运行您的初始查询WHERE classifications.IsTopHome == 1
  4. 享受

您的查询对于让LIKE操作员降低您的性能来说太重要了。
如果stories更新很多,我不认为你的classifications桌子是这样的。所以给你一个机会,消灭LIKE运营商。

于 2013-04-12T13:24:08.723 回答
0

您可以在这里尝试一些方法:

1) create covering index on classifications.`name`

您可以通过创建覆盖索引来加快查询速度。

覆盖索引是指查询中选择的所有字段都被索引覆盖的情况,在这种情况下,InnoDB(不是 MyISAM)永远不会读取表中的数据,而只会使用索引中的数据,显着加快选择。

CREATE TABLE classifications( KEY class_name( name,...all columns) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2)而不是classifications.name LIKE 'Home:Top%' 使用locate('Home:Top',classifications.name)

于 2013-04-17T13:08:05.440 回答