【问题标题】:How can I optimize a Mysql query that searches for rows in a certain date range如何优化在特定日期范围内搜索行的 Mysql 查询
【发布时间】:2011-08-05 05:51:07
【问题描述】:

这里是查询:

select timespans.id as timespan_id, count(*) as num
 from reports, timespans
 where  timespans.after_date >= '2011-04-13 22:08:38' and
        timespans.after_date <= reports.authored_at and
        reports.authored_at < timespans.before_date
 group by timespans.id;

这里是表格定义:

创建表`报告`( `id` int(11) NOT NULL auto_increment, `source_id` int(11) 默认 NULL, `url` varchar(255) 默认为 NULL, `lat` 十进制(20,15) 默认 NULL, `lng` 十进制(20,15) 默认 NULL, `内容`文本, `notes` 文本, `authored_at` 日期时间默认 NULL, `created_at` 日期时间默认 NULL, `updated_at` 日期时间默认 NULL, `数据`文本, `title` varchar(255) 默认 NULL, `author_id` int(11) 默认 NULL, `orig_id` varchar(255) 默认 NULL, 主键(`id`), KEY `index_reports_on_title`(`title`), KEY `index_content_on_reports` (`content`(128)) 创建表`时间跨度`( `id` int(11) NOT NULL auto_increment, `after_date` 日期时间默认 NULL, `before_date` 日期时间默认 NULL, `after_offset` int(11) 默认 NULL, `before_offset` int(11) 默认 NULL, `is_common` tinyint(1) 默认 NULL, `created_at` 日期时间默认 NULL, `updated_at` 日期时间默认 NULL, `is_search_chunk` tinyint(1) 默认为 NULL, `is_day` tinyint(1) 默认 NULL, 主键(`id`), KEY `index_timespans_on_after_date`(`after_date`), KEY `index_timespans_on_before_date`(`before_date`)

这里是解释:

+----+-------------+------------+-------+---------- -------------------------------------------------- --+-------------------+---------+------ -+--------+---------------------------------------------------- --------+ |编号 |选择类型 |表|类型 |可能的键 |关键 | key_len |参考 |行 |额外 | +----+-------------+------------+-------+---------- -------------------------------------------------- --+-------------------+---------+------ -+--------+---------------------------------------------------- --------+ | 1 |简单 |时间跨度 |范围 | index_timespans_on_after_date,index_timespans_on_before_date | index_timespans_on_after_date | 9 |空 | 84 |使用哪里;使用临时的;使用文件排序 | | 1 |简单 |报告 |全部 |空 |空 |空 |空 | 183297 |使用位置 | +----+-------------+------------+-------+---------- -------------------------------------------------- --+-------------------+---------+------ -+--------+---------------------------------------------------- --------+

这是我在 authored_at 上创建索引后的解释。如您所见,索引实际上并没有被使用(我认为...)

+----+-------------+------------+-------+---------- -------------------------------------------------- --+-------------------+---------+------ -+--------+---------------------------------------------------- ---------+ |编号 |选择类型 |表|类型 |可能的键 |关键 | key_len |参考 |行 |额外 | +----+-------------+------------+-------+---------- -------------------------------------------------- --+-------------------+---------+------ -+--------+---------------------------------------------------- ---------+ | 1 |简单 |时间跨度 |范围 | index_timespans_on_after_date,index_timespans_on_before_date | index_timespans_on_after_date | 9 |空 | 86 |使用哪里;使用临时的;使用文件排序 | | 1 |简单 |报告 |全部 | index_reports_on_authored_at |空 |空 |空 | 183317 |检查每条记录的范围(索引图:0x8)| +----+-------------+------------+-------+---------- -------------------------------------------------- --+-------------------+---------+------ -+--------+---------------------------------------------------- ---------+

报告表中有大约 142k 行,而时间跨度表中的行数要少得多。

现在查询大约需要 3 秒。

奇怪的是,如果我在reports.authored_at 上添加索引,它实际上会使查询慢得多,大约20 秒。我原以为它会做相反的事情,因为它可以很容易地找到范围两端的报告,然后把其余的扔掉,而不必检查所有报告。

有人可以澄清一下吗?我被难住了。

【问题讨论】:

  • 请说明结果和表格定义,tkx
  • reports.authored_at 上确实应该有一个索引。 EXPLAIN 在该列被索引后会说什么?

标签: mysql


【解决方案1】:

尝试将时间跨度表合并为一个多列索引,而不是两个单独的索引,而 before_date 和 after_date 在一个索引中。然后将该索引也添加到 authored_at 中。

【讨论】:

    【解决方案2】:

    我这样重写你的查询:

    select t.id, count(*) as num from timespans t 
      join reports r where t.after_date >= '2011-04-13 22:08:38' 
      and r.authored_at >= '2011-04-13 22:08:38' 
      and r.authored_at < t.before_date 
    group by t.id order by null;
    

    并更改表的索引

    alter table reports add index authored_at_idx(authored_at);
    

    【讨论】:

    • 太棒了!虽然 r.authored at 应该与 t.after_date 而不是文字值进行比较。但这肯定会解决它。据我所知,唯一真正的区别是比较的方向,将 r.authored_at 放在左侧使其更快。我不知道这有什么不同!
    • @user707270 你可以认为mysql没那么聪明知道t.authored>=t.after_date 和r.authored一样>='2011-04-13 22:08:38'
    【解决方案3】:

    您可以在after_date 列上使用数据库的分区功能。对你有很大帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-01-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-29
      相关资源
      最近更新 更多