2009-02-18 119 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小時的記錄...我不知道這是如何執行相對於空間搜索方法,因爲我將不得不爲去嘗試一下吧。)