5

我有一个包含大约 1 亿条记录的大表,其中包含字段和start_date类型。我需要检查某个日期范围的重叠数量,比如在AND之间,所以我使用。end_dateDATE2013-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)。结果,10m 行表的执行时间从 7 秒开始 - 变为 0.050 秒。

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

谢谢

4

3 回答 3

4

这是 表分区的完美用例。如果 Oracle INTERVAL 特性适用于 MySQL,那么它只会增加它的魅力。

于 2013-08-25T15:56:51.760 回答
2

日期列被索引

什么类型的索引?基于哈希的索引不适用于范围查询。如果它不是 BTREE 索引,那么现在就更改它。而且您还没有向我们展示 *它们是如何被索引的。两列是否在同一个索引中?里面还有其他东西吗?什么顺序(end_date 必须出现在第一列)?

脚本中有隐式类型转换 - 这应该由优化器自动处理,但值得检查......

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

如果我添加一列 is_future - TINYINT

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

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

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

此外,优化器不可能在不被强制的情况下使用这个索引(由于低基数)。

于 2013-08-26T08:56:27.787 回答
0

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

http://www.sqlfiddle.com/#!2/514ab/1/0 和计数 http://www.sqlfiddle.com/#!2/514ab/2/0

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

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

于 2013-08-25T15:30:47.997 回答