2009-02-18 123 views
3

假设表格如下:优化查询选择一段

Table events 
id 
start_time 
end_time 

有没有办法为恒定的快速搜索?

E.g.

SELECT * 
FROM events 
WHERE start_time<='2009-02-18 16:27:12' 
AND  end_time>='2009-02-18 16:27:12' 

我正在使用MySQL。有一个领域的索引仍然需要检查一个范围。此外,两个领域的索引不会有什么区别(只有第一个将被使用)。

我可以添加字段/索引到表中(因此添加包含两个字段的信息的索引构造字段将是可接受的)。

P.S.这个问题的需要来自这个问题:Optimize SQL that uses between clause

回答

6

有一个警告,以我的解决方案:

1)需要说明的该解决方案是,你必须使用针对该事件表中的MyISAM引擎。如果你不能使用MyISAM,那么这个解决方案将无法工作,因为只有MyISAM支持空间索引。

因此,假设上面是不是你的问题,下面应该工作,给你不错的表现:

该解决方案利用了MySQL的空间数据支持(见documentation here)。尽管可以将空间数据类型添加到各种存储引擎,但只有MyISAM才支持Spatial R-Tree索引(请参阅documentation here),这些索引是获得所需性能所必需的。另一个限制是空间数据类型仅适用于数字数据,因此您不能在基于字符串的范围查询中使用此技术。

我不会深入了解空间类型如何工作以及空间索引如何有用的理论细节,但您应该看看Jeremy Cole's explanation here关于如何使用空间数据类型和索引进行GeoIP查找。如果你需要原始的表现并且可以放弃一些准确性,那么看看他们提出的一些有用的观点和备选方案。

基本前提是我们可以采用开始/结束并使用它们中的两个创建四个不同的点,一个用于以xy网格为中心,以0,0为中心的矩形的每个角,然后快速完成查找空间索引以确定我们关心的特定时间点是否在矩形内。如前所述,请参阅Jeremy Cole的解释,以更全面地了解其工作原理。

在您的特定情况下,我们需要做到以下几点:

1)改变表是一个MyISAM表(注意你不应该这样做,除非你完全了解这种变化的后果比如缺少事务以及与MyISAM关联的表锁定行为)。

alter table events engine = MyISAM; 

2)接下来我们添加一个新的列,它将保存空间数据。我们将使用多边形数据类型,因为我们需要能够保存一个完整的矩形。

alter table events add column time_poly polygon NOT NULL; 

3)接下来我们填充数据的新列(请记住,该更新或插入到表事件将需要的任何进程得到修改,以确保它们也填充新列)。由于开始和结束范围是时间,所以我们需要使用unix_timestamp函数将它们转换为数字(有关它的工作原理,请参阅documentation here)。

update events set time_poly := LINESTRINGFROMWKB(LINESTRING(
    POINT(unix_timestamp(start_time), -1), 
    POINT(unix_timestamp(end_time), -1), 
    POINT(unix_timestamp(end_time), 1), 
    POINT(unix_timestamp(start_time), 1), 
    POINT(unix_timestamp(start_time), -1) 
)); 

4)接下来,我们空间索引添加到表中(如前面提到的,这将只对一个MyISAM表工作,并会产生错误“ERROR 1464(HY000):所使用的表型不支持SPATIAL索引“)。

​​

5)接下来,您需要使用以下select来在查询数据时使用空间索引。

​​

强制索引是让100%确定MySQL将使用该索引进行查找。如果一切顺利运行上述选择解释应该显示类似如下的内容:

mysql> explain SELECT * 
    -> FROM events force index (IXs_time_poly) 
    -> on MBRCONTAINS(events.time_poly, POINTFROMWKB(POINT(unix_timestamp('2009-02-18 16:27:12'), 0))); 
+----+-------------+-------+-------+---------------+---------------+---------+------+------+-------------+ 
| id | select_type | table | type | possible_keys | key   | key_len | ref | rows | Extra  | 
+----+-------------+-------+-------+---------------+---------------+---------+------+------+-------------+ 
| 1 | SIMPLE  | B  | range | IXs_time_poly | IXs_time_poly | 32  | NULL | 1 | Using where | 
+----+-------------+-------+-------+---------------+---------------+---------+------+------+-------------+ 
1 row in set (0.00 sec) 

请参考杰里米·科尔的分析,有关此方法的性能优势细节,该条款进行比较。

让我知道如果您有任何问题。

感谢,

-Dipin

2

MySQL没有有效的方法来完成此查询。

如果您的范围不重叠,但您可以只使用start_time <= const以及ORDER BY start_time DESC LIMIT 1并进一步检查end_time >= const

您需要在函数中执行此操作,因为MySQL出于某种原因,如果范围条件取自超查询,则在子查询中不会使用INDEX RANGE SCAN代替ORDER BY

CREATE UNIQUE INDEX ux_b_start ON b (start_date); 

CREATE FUNCTION `fn_get_last_b`(event_date TIMESTAMP) RETURNS int(11) 
BEGIN 
    DECLARE id INT; 
    SELECT b.id 
    INTO id 
    FROM b 
    FORCE INDEX (ux_b_start) 
    WHERE b.start_time <= event_date 
    ORDER BY 
    b.start_time DESC 
    LIMIT 1; 
    RETURN id; 
END; 

SELECT COUNT(*) FROM a; 

1000 


SELECT COUNT(*) FROM b; 

200000 

SELECT * 
FROM (
    SELECT fn_get_last_b(a.event_time) AS bid, 
     a.* 
    FROM a 
) ao, b FORCE INDEX (PRIMARY) 
WHERE b.id = ao.bid 
    AND b.end_time >= ao.event_time 

1000 rows fetched in 0,0143s (0,1279s) 
+0

你的“start_time <= const以及ORDER BY start_time DESC LIMIT 1”是一个非常好的主意。由于start_date键似乎非常有效地使用,因此性能良好。剩下的答案应该发布在我发布的其他问题上! – daremon 2009-02-21 12:30:16

+0

它也张贴在那里:) – Quassnoi 2009-02-21 23:18:37

-1

在一个表格中没有太多可以做的事。如果优化这些查询1)需要2)必须在SQL级上完成,那么你就需要做一个派生表:

Table event_times 
id 
event_id 
mark_time 

和记录添加到它的每一个跨越每一个时间单位事件。然后你只需

SELECT * 
FROM events 
LEFT JOIN event_times ON event_id = events.id 
WHERE mark_time = '2009-02-18 16:27:12' 

您可以将此表通过你如何定义“单位时间”,即如果限制mark_time的分辨率几分钟或几小时而不是秒的好少一点可笑。

0

我对MySQL没有太多的经验,但是在MS SQL Server上,在两行上添加一个索引,允许在1M行表上进行索引查找和返回时间从1-2秒变为毫秒响应时间。

看来你看到了不同的结果。我想知道一个约束是否会产生差异。我有一个检查约束来执行start_time < end_time。

+0

在这种情况下,MS SQL使用“索引组合”。它使用两个索引选择两个范围,并使用散列连接查找交集。如果你把一个既有很多start_times又有很多end_times的常量满足适当的条件,这将是最低效的情况。 – Quassnoi 2009-02-18 16:04:29

0

你基本上已经有了一个查询与2个明显分开的范围条件。你正在使用> =,对MySQL来说,这总是一个范围扫描。有文档here优化范围扫描。

底线是MySQL执行额外的检查来筛选满足范围条件的行,然后满足WHERE子句的其余部分,在您的情况下是其他范围条件。

0

我要问一个类似的问题,优化了事件的搜索(项目进行启动&停止时间),并且我已经使用了不同的方法,所以我会把它扔到那里。

基本上,如果你知道你的事件永远不会超过给定的持续时间,你可以搜索一个大于最大持续时间的有界范围,然后添加限制来摆脱匹配的额外东西。因此,要获得与搜索时间相交时间:

SELECT * 
FROM events 
WHERE 
    (start_time BETWEEN ('search_start' - INTERVAL 2 DAY) and 'search_end') 
    AND end_time >= 'search_start' 

...你会希望有start_time的索引。 (注意 - 我的桌子上有数百万的事件分布在4年以上,没有超过24小时的记录...我不知道这是如何执行相对于空间搜索方法,因为我将不得不为去尝试一下吧。)