2012-01-05 4 views
0

У меня есть таблица (MySQL), содержащей даты последнего сканирования хостов в сочетании с отчетом ID:Оптимизировать вложенный запрос для одного запроса

+--------------+---------------------+--------+ 
| host   | last_scan   | report | 
+--------------+---------------------+--------+ 
| 112.86.115.0 | 2012-01-03 01:39:30 |  4 | 
| 112.86.115.1 | 2012-01-03 01:39:30 |  4 | 
| 112.86.115.2 | 2012-01-03 02:03:40 |  4 | 
| 112.86.115.2 | 2012-01-03 04:33:47 |  5 | 
| 112.86.115.1 | 2012-01-03 04:20:23 |  5 | 
| 112.86.115.6 | 2012-01-03 04:20:23 |  5 | 
| 112.86.115.2 | 2012-01-05 04:29:46 |  8 | 
| 112.86.115.6 | 2012-01-05 04:17:35 |  8 | 
| 112.86.115.5 | 2012-01-05 04:29:48 |  8 | 
| 112.86.115.4 | 2012-01-05 04:17:37 |  8 | 
+--------------+---------------------+--------+ 

Я хочу, чтобы выбрать список всех узлов с датой последнего сканирования и соответствующего идентификатора отчета. Я построил следующий вложенный запрос, но я уверен, что это можно сделать в одном запросе:

SELECT rh.host, rh.report, rh.last_scan 
FROM report_hosts rh 
WHERE rh.report = (
    SELECT rh2.report 
    FROM report_hosts rh2 
    WHERE rh2.host = rh.host 
    ORDER BY rh2.last_scan DESC 
    LIMIT 1 
) 
GROUP BY rh.host 

Можно ли сделать это с помощью одного, невложенного запроса?

ответ

3

Нет, но вы можете сделать JOIN в запросе

SELECT x.* 
FROM report_hosts x 
INNER JOIN (
    SELECT host,MAX(last_scan) AS last_scan FROM report_hosts GROUP BY host 
) y ON x.host=y.host AND x.last_scan=y.last_scan 

Ваш запрос делает FileSort, что очень неэффективно. У моих решений нет. Очень полезно создать индекс в этой таблице.

ALTER TABLE `report_hosts` ADD INDEX (`host` , `last_scan`) ; 

Иначе ваш запрос будет делать файлы в два раза.

+0

Это кажется разумным. Спасибо за индекс! –

0

Если вы хотите выбрать из таблицы report_hosts только один раз, вы можете использовать своего рода метод «RANK OVER PARTITION» (доступный в Oracle, но, к сожалению, в MySQL). Нечто подобное должно работать:

select h.host,h.last_scan as most_recent_scan,h.report 
from 
(
select rh.*, 
case when @curHost != rh.host then @rank := 1 else @rank := @rank+1 end as rank, 
case when @curHost != rh.host then @curHost := rh.host end 
from report_hosts rh 
cross join (select @rank := null,@curHost = null) t 
order by host asc,last_scan desc 
) h 
where h.rank = 1; 

Конечно это все еще вложенными, но это избежать «двойного выбора» проблему. Не уверен, будет ли он более эффективным или нет - зависит от того, какие индексы у вас есть и объем данных.

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