2011-11-20 60 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秒。 –