按日期搜索mysql性能

时间:2013-08-25 14:47:14

标签: mysql

我有一个包含大约1亿条记录的大型表格,其中包含start_dateend_date字段,DATE类型。我需要检查一些日期范围的重叠次数,例如在2013-08-202013-08-30之间,所以我使用。

SELECT COUNT(*) FROM myTable WHERE end_date >= '2013-08-20' 
AND start_date <= '2013-08-30'

日期列已编入索引。 重要的一点是,我搜索重叠的日期范围总是在将来,而表中记录的主要部分是过去的(比如大约97-99百万)。 所以,如果我添加一列is_future - TINYINT,这个查询会更快,所以,只检查这样的条件

SELECT COUNT(*) FROM myTable WHERE is_future = 1 
AND end_date >= '2013-08-20' AND start_date <= '2013-08-30'

它将排除剩余的9700万左右的记录,并仅检查剩余的1-3百万条记录的日期条件?

我使用MySQL

由于

修改

mysql引擎是innodb,但如果说是MyISAM

则会很重要

这是创建表

CREATE TABLE `orders` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `title`
  `start_date` date DEFAULT NULL,
  `end_date` date DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
@Robert Co回答后

编辑2

对于这种情况,分区看起来是个好主意,但它不允许我创建基于is_future字段的分区,除非我将其定义为主键,否则我应该删除我的主要主键 - id,这是我做不到的。因此,如果我将该字段定义为主键,那么是否存在分区的含义,如果我通过主键is_future字段进行搜索,则不会很快。

编辑3 我需要使用它的实际查询是选择具有该日期范围的一些免费表的餐馆

SELECT r.id, r.name, r.table_count
FROM restaurants r
LEFT JOIN orders o 
ON r.id = o.restaurant_id 
WHERE o.id IS NULL 
OR (r.table_count > (SELECT COUNT(*) 
                FROM orders o2 
                WHERE o2.restaurant_id = r.id AND
                end_date >= '2013-08-20' AND start_date <= '2013-08-30'
                AND o2.status = 1
            )
) 

经过更多的研究和测试,在我的情况下计算行数的最快方法是再添加一个条件,start_date超过当前日期(因为搜索的日期范围总是在将来)

 SELECT COUNT(*) FROM myTable WHERE end_date >= '2013-09-01' 
         AND start_date >= '2013-08-20' AND start_date <= '2013-09-30'

还有一个索引 - 使用start_date和end_date字段(谢谢@symcbean)。 因此,从7秒开始的10m行的表上执行时间变为0.050秒。

解决方案2 (@Robert Co) 在这种情况下分区工作也一样! - 也许它比索引更好的解决方案。或者它们都可以一起使用。

由于

3 个答案:

答案 0 :(得分:4)

这是一个完美的用例 table partitioning。如果Oracle INTERVAL功能使其成为MySQL,那么它只会增加到令人敬畏的功能。

答案 1 :(得分:2)

  

日期列已编入索引

什么类型的索引?基于散列的索引不适用于范围查询。如果它不是BTREE索引,那么现在就改变它。你没有向我们展示* 他们被索引的方式。这两列是否在同一个索引中?那里还有其他的东西吗?什么顺序(end_date必须作为第一列出现)?

脚本中存在隐式类型转换 - 优化器会自动处理 ,但值得检查....

SELECT COUNT(*) FROM myTable WHERE end_date >= 20130820000000 
AND start_date <= 20130830235959
  

如果我添加一列is_future - TINYINT

首先,为了有用,这将要求未来日期占表中存储的总数据的一小部分(小于10%)。而这只是为了使它比全表扫描更有效。

其次,它需要非常频繁地更新索引来维护它,除了初始populatiopn的开销之外,可能会导致索引碎片化和性能下降(取决于如何构建iondex)。

第三,如果仍然需要处理300万行数据(特别是通过索引查找),那么即使数据挂在内存中,它也会非常慢。

此外,优化器永远不会使用此索引而不会被强制使用(由于基数较低)。

答案 2 :(得分:0)

我做了一个简单的测试,只是在tinyint列上创建了一个索引。结构可能不一样,但有索引似乎有效。

http://www.sqlfiddle.com/#!2/514ab/1/0 并计算 http://www.sqlfiddle.com/#!2/514ab/2/0

在那里查看执行计划,看看select只扫描一行,这意味着它只处理你案例中较少数量的记录。

所以简单的答案是肯定的,有一个索引就可以了。