2011-11-20 6 views
1

Iは、以下のスキーマで3つのテーブルを有する:MAXとMySQLのクエリ最適化()

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`) 
) 

設定は、デバイスが1つまたは複数のチャネルを有することができることであり、各チャネルは、多くの(過去の)データを有しています。

私は1つのデバイスと、それがチャンネルを関連だすべての最後の履歴データを取得するには、次のクエリを使用

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: Mutlipleチャネル例SELECT:

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で文法的にプロを生成することができますが、必要に応じて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すなわち最大を返す必要がありますとデータ。結果は、1つのデバイスの各チャネルのhistorical_dataテーブルの最後のレコードになります。

+0

トピック:グループ:c.channel_id、c.chann el、h.data – danihp

+0

「最後の履歴データ」の意味をさらに説明できますか?これらのテーブルのサイズをランク付けできますか?最大のものは?クエリの変更を提案する前に、まずこれらのいくつかのことを知っておくと良いでしょう。次に、サンプルデータを提供できますか?私はあなたが 'max(h.date_time)'だけを取得しているときに 'h.data'の必要性について疑問に思っています。あなたは大量の情報を提供してくれました。ちょっとだけ!ありがとう! :) – Nonym

+0

historical_dataテーブルにはセンサからの読み取り値が含まれており、現在300,000以上の行があります。 channelsテーブルには19個のレコードとデバイスがあります。特定のデバイスのhistory_dataテーブルでdatetimeで最後のレコードを取得して、同じセンサーからの新しい読み取り値と比較できます。 –

答えて

1

それをそれを削除して再度作成することによってdate_timeのインデックスを再作成することは、元のSQLを約2秒まで高速化したようです。

+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'); 

を使用すると、1つのデバイスのためにこれを使用して、所望の結果を得る場合でも、あなたが他のデバイスとそれを試すことができるように、私は、ストアドプロシージャにそれを働いて試してみました結果を見る...ありがとう!

[編集]:

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、name)にインデックスを追加してクエリに一致させます。私はこのテーブルにいくつのエントリがあるのか​​分かりませんが、デバイスごとに多くの要素がある場合は、それを正しく理解してください。

(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とchannel from channelsテーブルが必要なので正しい結果を返さなかった。また、制限は1つの結果に制限しますが、私は各チャンネルのすべての最後のレコードが必要です。私は最適化しようとしている選択ステートメントからの例の結果で説明を更新しました。私はあなたの助けをprevaricateする –

+0

助けていただきありがとうございますが、あなたが追加されたインデックスで私に与えた質問は約25-30秒かかりました。 –