2014-12-01 5 views
0

В таблице:Оптимизация вложенных запросов (в частности, GROUP BY) в MySQL

CREATE TABLE `temperature` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 
    `hive_id` int(10) unsigned NOT NULL, 
    `value` decimal(4,1) NOT NULL, 
    `created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', 
    `updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', 
    PRIMARY KEY (`id`), 
    UNIQUE KEY `idplusdate` (`hive_id`,`created_at`), 
    KEY `hive_id` (`hive_id`) 
) ENGINE=InnoDB AUTO_INCREMENT=360001 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci 

Запрос:

SELECT 
      hives.guid as hive_guid, 

      temperature.id as Temperature_id, 
      temperature.hive_id as Temperature_hive_id, 
      temperature.value as Temperature_value, 
      temperature.created_at as Temperature_created_at, 
      temperature.updated_at as Temperature_updated_at 

FROM hives 

INNER JOIN (
      SELECT 
       *, 
       @num := if(@hive_id = hive_id, @num + 1, 1) as row_number, 
       @hive_id := hive_id as dummy 
      FROM 
        (SELECT * 
        FROM temperature FORCE INDEX (idplusdate) 
        ORDER BY hive_id, created_at desc) T 
      GROUP BY hive_id, created_at 
      HAVING row_number <= 2 
     ) temperature 
ON hives.id = temperature.hive_id 

WHERE hives.guid IN ('tfdb3560-200a-45f7-ab0e-d699fty8w9b9'); 

Объяснение:

id select_type table type possible_keys key key_len ref rows Extra 
1 PRIMARY hives ref PRIMARY,hives_guid_index hives_guid_index 110 const 1 Using where; Using index 
1 PRIMARY <derived2> ref <auto_key0> <auto_key0> 4 XXX.hives.id 359 NULL 
2 DERIVED <derived3> ALL NULL NULL NULL NULL 359640 Using temporary; Using filesort 
3 DERIVED temperature ALL NULL NULL NULL NULL 359640 Using filesort 

Хорошо, так что я есть таблица hives с элементами с GUID (не очень важно для этого запроса). У меня также есть таблица temperature, которая содержит несколько показаний датчиков от каждого улья. Целью запроса является получение последнего N (в данном случае 2) показаний датчика для конкретного GUID (помните, что этот запрос будет использоваться с несколькими GUID, поэтому я использую WHERE IN). Я знаю, что запрос является немного сложным для такой банальной задачи, но это лучшее, что я нашел для больших наборов данных (если у вас есть какие-либо предложения, пожалуйста, поделитесь)

Ожидаемый результат в этом случае:

tfdb8560-200a-45f7-ab0e-d699fty8w9b9 2879 8 29.6 9/28/2014 12:00 9/28/2014 12:00 
tfdb3560-200a-45f7-ab0e-d699fty8w9b9 2880 8 26.6 9/28/2014 18:00 9/28/2014 18:00 

Поскольку таблица имеет много строк (в этом случае ожидается, что в этом случае ожидается 360 тыс.), Запрос выполняет 3-4 секунды. На этот раз я хочу снизить, и я определил GROUP BY как главного виновника в течение длительного времени (поскольку у него, очевидно, нет никакого индекса для группировки).

Поэтому я бы использовал любые подходы к улучшению времени запроса, если конечный результат тот же. Спасибо!

ответ

1

Ваш запрос слишком сложный. Если я правильно понимаю, вам не нужен group by. Вот альтернативный вариант пункта FROM:

FROM hives INNER JOIN 
    (SELECT t.*, 
      (@num := if(@hive_id = hive_id, @num + 1, 
         if(@hive_id := hive_id, 1, 1) 
      ) as row_number 
     FROM temperature t CROSS JOIN 
      (select @num := 0, @hive_id := '') vars 
     ORDER BY hive_id, created_at desc 
    ) temperature 
    ON hives.id = temperature.hive_id and temperature.row_number <= 2; 

Обратите внимание, что я ставлю все присвоения переменных в одном выражении. MySQL не гарантирует порядок оценки выражений в SELECT. Ваша первоначальная версия зависела от row_number, которую оценивали до dummy.

+0

Отлично, большое вам спасибо. Немного не по теме - можете ли вы предложить книгу (или другой тип источника) для освоения таких промежуточных/расширенных запросов? –

Смежные вопросы