【问题标题】:mysql where + group by very slowmysql where + group by 很慢
【发布时间】:2012-11-14 21:47:52
【问题描述】:

一个我应该可以自己回答的问题,但我没有,而且我也没有在 google 中找到任何答案:

我有一个包含 500 万行的表,结构如下:

CREATE TABLE IF NOT EXISTS `files_history2` (
  `FILES_ID` int(10) unsigned DEFAULT NULL,
  `DATE_FROM` date DEFAULT NULL,
  `DATE_TO` date DEFAULT NULL,
  `CAMPAIGN_ID` int(10) unsigned DEFAULT NULL,
  `CAMPAIGN_STATUS_ID` int(10) unsigned DEFAULT NULL,
  `ON_HOLD` decimal(1,0) DEFAULT NULL,
  `DIVISION_ID` int(11) DEFAULT NULL,
  KEY `DATE_FROM` (`DATE_FROM`),
  KEY `FILES_ID` (`FILES_ID`),
  KEY `CAMPAIGN_ID` (`CAMPAIGN_ID`),
  KEY `CAMP_DATE` (`CAMPAIGN_ID`,`DATE_FROM`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

当我执行时

SELECT files_id, min( date_from )
FROM files_history2
WHERE campaign_id IS NOT NULL
GROUP BY files_id

查询停留在“发送数据”状态超过八小时(然后我终止了该进程)。

这里解释一下:

id  select_type     table           type    possible_keys           key     key_len     ref     rows        Extra
1   SIMPLE          files_history2  ALL     CAMPAIGN_ID,CAMP_DATE   NULL    NULL        NULL    5073254     Using where; Using temporary; Using filesort

我假设我生成了必要的键,但是查询应该花费那么长时间,是吗?

【问题讨论】:

  • 这是说5073254 行吗?尝试在那里设置限制。
  • 应该不会那么糟糕。索引旨在帮助加快查询速度。一定是别的什么在起作用。可能是配置错误,导致 mysql 内存不足。
  • 它将转到filesort,而不是使用任何索引。这是有原因的,但不幸的是我还不够聪明。

标签: mysql group-by key


【解决方案1】:

我建议使用不同的索引...索引 (Files_ID, Date_From, Campaign_ID)...

由于您的分组依据是 Files_ID,因此您希望将 THOSE 分组。然后是 MIN(Date_From),所以它位于第二个位置......然后是 Campaign_ID 最终符合不为空的条件,这就是为什么......

如果您将所有广告系列 ID 放在首位,那太好了,将所有 NULL 都排除在外……现在,您有 1,000 个广告系列,而且 Files_ID 跨越许多广告系列,而且它们还跨越许多日期,您会窒息的。

根据我投影的索引,首先是 Files_ID,您已经为每个“files_id”排序以匹配您的组。然后,在其中,所有最早的日期都在索引列表的顶部……太好了,几乎在那里,然后,按活动 ID。跳过可能存在的任何 NULL 并完成,进入下一个 Files_ID

希望这是有道理的——除非你有大量的条目具有 NULL 值活动。

此外,通过使索引的所有 3 个部分与查询的条件和输出列匹配,它永远不必返回原始数据文件来获取数据,它直接从索引中获取所有数据。

【讨论】:

  • 效果更好,在阅读了您的说明后,我发现我仍然需要学习很多关于 MySQL 的知识。谢谢。
  • @holgrich,仅就我对不同解决方案的了解而言,最终性能时间如何。
【解决方案2】:

我会创建一个覆盖索引(CAMPAIGN_ID、files_id、date_from)并检查其性能。我怀疑您的问题是由于分组 not 和 date_from 无法使用相同的索引。

CREATE INDEX your_index_name ON files_history2 (CAMPAIGN_ID, files_id, date_from);

如果这可行,您可以删除点索引CAMPAIGN_ID,因为它包含在复合索引中。

【讨论】:

    【解决方案3】:

    由于聚合(函数 MIN )以及分组,查询速度很慢。 解决方案之一是通过将聚合子查询从 WHERE 子句移动到 FROM 子句来更改查询,这将比您使用的方法快得多。

    尝试以下:

    SELECT f.files_id 
    FROM file_history2 AS f 
    JOIN ( 
    SELECT campaign_id, MIN(date_from) AS datefrom 
    FROM file_history2 
    GROUP BY files_id 
    ) AS f1 ON f.campaign_id = f1.campaign_id AND f.date_from = f1.datefrom; 
    

    这应该有更好的性能,如果不工作临时表只能是选择。

    【讨论】:

    • 我不明白“campaign_id IS NOT NULL”包含在您的声明中的什么位置。我确保我正确地陈述了我的目标:对于所有 files_id 条目,有几个 date_to 条目具有不同的campaign_id。我需要最旧的 date_from,其中每个 files_id 的 campaipn_id 都不为空。
    猜你喜欢
    • 2021-12-26
    • 1970-01-01
    • 1970-01-01
    • 2012-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-13
    • 2017-11-07
    相关资源
    最近更新 更多