2015-02-12 3 views
0

У меня есть рабочий запрос, который кажется ужасно неэффективным; Мне интересно, если я пропустил простой способ улучшить его.mysql groupwise max как второй, где условие

Простая таблица:

 
id date  master_id 
------------------------- 
1 2015-02-01 0 
2 2015-02-02 0 
3 2015-02-03 0 
4 2015-02-04 1 
5 2015-02-02 1 
6 2015-02-17 1 
7 2015-02-27 1 
8 2015-01-01 1 

Цель: Получить все строки, где master_id равен нулю, ИЛИ master_id не равен нулю, и никакие другие строки того же master_id не имеют более раннюю дату. Заказывайте каждый результат по дате.

Текущий запрос, используя минимальный подзапрос в группе, чтобы создать второе условие WHERE.

 

SELECT * 
FROM `test` 
WHERE `master_id` =0 
OR `id` IN (

    SELECT test.`id` 
    FROM (
     SELECT `master_id`, MIN(`date`) AS mindate 
     FROM `test` 
     WHERE `master_id` 0  
     GROUP BY `master_id` 
    ) AS x 
    INNER JOIN `test` ON x.`master_id` = test.`master_id` 
    AND x.mindate= test.`date` 
) 
ORDER BY `date` 

Он работает, но EXPLAIN делает это, кажется неэффективным:

 
id select_type   table  type possible_keys key   key_len  ref  rows Extra 
------------------------------------------------------------------------------------------------------------- 
1 PRIMARY    test  ALL  NULL   NULL  NULL  NULL 8  Using where; Using filesort 
2 DEPENDENT SUBQUERY derived3 system NULL   NULL  NULL  NULL 1 
2 DEPENDENT SUBQUERY test  eq_ref PRIMARY   PRIMARY  4   func 1  Using where 
3 DERIVED    test  ALL  NULL   NULL  NULL  NULL 8  Using where; Using temporary; Using filesort 

Могу ли я улучшить это? Должен ли я разбить его на два запроса, один для ID = 0 и один для группового min? Заранее спасибо.

+0

я иногда нахожу, что имеющие отдельный 'select's помогает скорости. –

+0

Получил sqlfiddle для этого? – Strawberry

+0

@Strawberry Уверенная вещь: http://sqlfiddle.com/#!2/db484/2/0 – mike

ответ

0

избежать внутреннего соединения может улучшить запрос:

SELECT * 
FROM `test` 
WHERE `master_id` =0 
OR `id` IN (
    SELECT t1.id 
    FROM (SELECT * 
     FROM test t2 
     WHERE t2.master_id!=0 
     ORDER BY t2.date ASC) t1 
    GROUP BY t1.master_id 
) 
ORDER BY `date`; 
+0

Это выглядит хорошо; будет ли первый подзапрос всегда выбирать идентификатор первой строки из второго подзапроса, соблюдая его порядок? Когда mysql группирует строки без каких-либо агрегатных функций, всегда ли они выбирают первое значение, которое он находит для каждого столбца, который соответствует группе по условию? – mike

0

Как об этом ...

SELECT * FROM test WHERE master_id = 0 
UNION 
SELECT x.* 
    FROM test x 
    JOIN (SELECT master_id,MIN(date) min_date FROM test GROUP BY master_id) y 
    ON y.master_id = x.master_id 
    AND y.min_date = x.date;