2011-11-20 61 views
1

我有3個表有以下模式:MySQL查詢優化的MAX()

CREATE TABLE `devices` (
    `device_id` int(11) NOT NULL auto_increment, 
    `name` varchar(20) default NULL, 
    `appliance_id` int(11) default '0', 
    `sensor_type` int(11) default '0', 
    `display_name` VARCHAR(100), 
    PRIMARY KEY USING BTREE (`device_id`) 
) 

CREATE TABLE `channels` (
    `channel_id` int(11) NOT NULL AUTO_INCREMENT, 
    `device_id` int(11) NOT NULL, 
    `channel` varchar(10) NOT NULL, 
    PRIMARY KEY (`channel_id`), 
    KEY `device_id_idx` (`device_id`) 
) 

CREATE TABLE `historical_data` (
    `date_time` datetime NOT NULL, 
    `channel_id` int(11) NOT NULL, 
    `data` float DEFAULT NULL, 
    `unit` varchar(10) DEFAULT NULL, 
    KEY `devices_datetime_idx` (`date_time`) USING BTREE, 
    KEY `channel_id_idx` (`channel_id`) 
) 

的設置是一個設備可以具有一個或多個通道,每個通道具有許多(歷史的)的數據。

我用下面的查詢來獲取一個設備的最後歷史數據以及所有它的相關渠道:

SELECT c.channel_id, c.channel, max(h.date_time), h.data 
FROM devices d 
INNER JOIN channels c ON c.device_id = d.device_id 
INNER JOIN historical_data h ON h.channel_id = c.channel_id 
WHERE d.name = 'livingroom' AND d.appliance_id = '0' 
AND d.sensor_type = 1 AND (c.channel = 'ch1') 
GROUP BY c.channel 
ORDER BY h.date_time, channel 

查詢計劃如下所示:

+----+-------------+-------+--------+-----------------------+----------------+---------+---------------------------+--------+-------------+ 
| id | select_type | table | type | possible_keys   | key   | key_len | ref      | rows | Extra  | 
+----+-------------+-------+--------+-----------------------+----------------+---------+---------------------------+--------+-------------+ 
| 1 | SIMPLE  | c  | ALL | PRIMARY,device_id_idx | NULL   | NULL | NULL      |  34 | Using where | 
| 1 | SIMPLE  | d  | eq_ref | PRIMARY    | PRIMARY  | 4  | c.device_id    |  1 | Using where | 
| 1 | SIMPLE  | h  | ref | channel_id_idx  | channel_id_idx | 4  | c.channel_id    | 322019 |    | 
+----+-------------+-------+--------+-----------------------+----------------+---------+---------------------------+--------+-------------+ 
3 rows in set (0.00 sec) 

以上查詢是目前大約需要15秒,我想知道是否有任何提示或方法來改善查詢?

編輯:從historical_data 實施例數據

+---------------------+------------+------+------+ 
| date_time   | channel_id | data | unit | 
+---------------------+------------+------+------+ 
| 2011-11-20 21:30:57 |   34 | 23.5 | C | 
| 2011-11-20 21:30:57 |   9 | 68 | W | 
| 2011-11-20 21:30:54 |   34 | 23.5 | C | 
| 2011-11-20 21:30:54 |   5 | 316 | W | 
| 2011-11-20 21:30:53 |   34 | 23.5 | C | 
| 2011-11-20 21:30:53 |   2 | 34 | W | 
| 2011-11-20 21:30:51 |   34 | 23.4 | C | 
| 2011-11-20 21:30:51 |   9 | 68 | W | 
| 2011-11-20 21:30:49 |   34 | 23.4 | C | 
| 2011-11-20 21:30:49 |   4 | 193 | W | 
+---------------------+------------+------+------+ 
10 rows in set (0.00 sec) 

編輯2: 複式信道選擇例如:

SELECT c.channel_id, c.channel, max(h.date_time), h.data 
FROM devices d 
INNER JOIN channels c ON c.device_id = d.device_id 
INNER JOIN historical_data h ON h.channel_id = c.channel_id 
WHERE d.name = 'livingroom' AND d.appliance_id = '0' 
AND d.sensor_type = 1 AND (c.channel = 'ch1' OR c.channel = 'ch2' OR c.channel = 'ch2') 
GROUP BY c.channel 
ORDER BY h.date_time, channel 

我使用或c.channel where子句,因爲它更容易以語法形式生成,但如果需要可以更改爲使用IN。

編輯3:我想要達到 結果舉例:

+-----------+------------+---------+---------------------+-------+ 
| device_id | channel_id | channel | max(h.date_time) | data | 
+-----------+------------+---------+---------------------+-------+ 
|  28 |   9 | ch1  | 2011-11-21 20:39:36 |  0 | 
|  28 |   35 | ch2  | 2011-11-21 20:30:55 | 32767 | 
+-----------+------------+---------+---------------------+-------+ 

我已經加入了DEVICE_ID的例子,但我的選擇將只需要返回CHANNEL_ID,通道,最後DATE_TIME即最多和數據。結果應該是history_data表中每個設備的每個通道的最後一條記錄。

+0

文章:group by by應該是:c.channel_id,c.chann el,h.data – danihp

+0

你能進一步描述你'最近的歷史數據'是什麼意思嗎?你可以按大小排列這些表嗎?像哪個是最大的?在建議任何查詢更改之前,先了解這幾件事情是很好的。那麼您能提供樣本數據嗎?我想知道的是,只有在獲得'max(h.date_time)'時才需要'h.data' ..你已經做得很好,提供了一大堆信息。只需要更多一點!謝謝! :) – Nonym

+0

historical_data表包含來自傳感器的讀數,目前有300,000多行。頻道表有19個記錄,而設備有10個。我想要根據特定設備的歷史數據表中的日期時間獲取最後一條記錄,以便我可以將它與來自同一傳感器的新讀數進行比較。 –

回答

1

它似乎刪除重新創建date_time索引通過刪除並重新創建它加快了我的原始SQL高達2secs左右

+0

感謝您的幫助。 –

0

我還沒有能夠測試這個,所以我想問你運行它,讓我們知道會發生什麼..如果它給你所需的結果,如果它比你當前的運行速度更快:

CREATE DEFINER=`root`@`localhost` PROCEDURE `GetLatestHistoricalData_EXAMPLE` 
    (
     IN param_device_name VARCHAR(20) 
    , IN param_appliance_id INT 
    , IN param_sensor_type INT 
    , IN param_channel VARCHAR(10) 
) 
BEGIN 

    SELECT 
     h.date_time, h.data 
    FROM 
     historical_data h 
     INNER JOIN 
     (
      SELECT c.channel_id 
      FROM devices d 
      INNER JOIN channels c ON c.device_id = d.device_id 
      WHERE 
       d.name = param_device_name 
      AND d.appliance_id = param_appliance_id 
      AND d.sensor_type = param_sensor_type 
      AND c.channel = param_channel 
     ) 
     c ON h.channel_id = c.channel_id 
    ORDER BY h.date_time DESC 
    LIMIT 1; 

END 

然後運行一個測試:

CALL GetLatestHistoricalData_EXAMPLE ('livingroom', 0, 1, 'ch1'); 

我努力的在它變成一個存儲過程,這樣即使你使用此一臺設備所期望的結果,你可以與其它設備嘗試,看到結果...謝謝!

[編輯]:在回答丹尼的評論這裏是一個更新的測試版本:

CREATE DEFINER=`root`@`localhost` PROCEDURE `GetLatestHistoricalData_EXAMPLE_3Channel` 
    (
     IN param_device_name VARCHAR(20) 
    , IN param_appliance_id INT 
    , IN param_sensor_type INT 
    , IN param_channel_1 VARCHAR(10) 
    , IN param_channel_2 VARCHAR(10) 
    , IN param_channel_3 VARCHAR(10) 
) 
BEGIN 

    SELECT 
     h.date_time, h.data 
    FROM 
     historical_data h 
     INNER JOIN 
     (
      SELECT c.channel_id 
      FROM devices d 
      INNER JOIN channels c ON c.device_id = d.device_id 
      WHERE 
       d.name = param_device_name 
      AND d.appliance_id = param_appliance_id 
      AND d.sensor_type = param_sensor_type 
      AND (
       c.channel IN (param_channel_1 
          ,param_channel_2 
          ,param_channel_3 
       ) 
     c ON h.channel_id = c.channel_id 
    ORDER BY h.date_time DESC 
    LIMIT 1; 

END 

然後運行一個測試:

CALL GetLatestHistoricalData_EXAMPLE_3Channel ('livingroom', 0, 1, 'ch1', 'ch2' , 'ch3'); 

同樣,這只是爲了測試,所以你將能夠看到它是否滿足您的需求..

+0

感謝您的回答。當我回來時我會嘗試,但我只是注意到我的示例查詢是不好的,因爲它沒有顯示多個頻道。我已經更新上面顯示一個SELECT多通道 –

+0

@DannyTsang我'添加'到我的文章,以便我通過的原始程序將保持不變,以便您可以檢查3個通道的相同邏輯。 – Nonym

0

我會首先在設備表(appliance_id,sensor_type,名稱)上添加一個索引以匹配您的查詢。我不知道這個表中有多少個條目,但是如果每個設備都有很大的元素,就可以正確使用它。

其次,在你的頻道表,索引上(DEVICE_ID,通道)

三,關於你的歷史數據,指數(CHANNEL_ID,DATE_TIME)

然後,

SELECT STRAIGHT_JOIN 
     PreQuery.MostRecent, 
     PreQuery.Channel_ID, 
     PreQuery.Channel, 
     H2.Data, 
     H2.Unit 
    from 
     (select 
       c.channel_id, 
       c.channel, 
       max(h.date_time) as MostRecent 
      from 
       devices d 

       join channels c 
        on d.device_id = c.device_id 
        and c.channel in ('ch1', 'ch2', 'ch3') 

        join historical_data h 
         on c.channel_id = c.Channel_id 
      where 
        d.appliance_id = 0 
       and d.sensor_type = 1 
       and d.name = 'livingroom' 

      group by 
       c.channel_id) PreQuery 

     JOIN Historical_Data H2 
     on PreQuery.Channel_ID = H2.Channel_ID 
     AND PreQuery.MostRecent = H2.Date_Time 
    order by 
     PreQuery.MostRecent, 
     PreQuery.Channel 
+0

我試過了您的查詢並且它沒有帶回正確的結果,因爲我需要channel_id和頻道表中的頻道。限制也限制爲一個結果,但我需要每個通道的最後一個記錄。我使用我試圖優化的select語句的示例結果更新了描述。我支持你的幫助 –

+0

感謝您的幫助,但您給我的查詢添加索引需要大約25-30秒。 –