2013-06-06 9 views
1

У меня есть запрос, который, как мне кажется, имеет довольно распространенный шаблон. Рассмотрим эту таблицу:Как избежать DISTINCT в качестве костыля при выполнении аналитических запросов?

id | val | ts 
---+-----+------- 
a | 10 | 12:01 
a | 12 | 12:05 
a | 9 | 12:15 
b | 30 | 12:03 

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

-- where in aggregate subquery 
-- we avoid this because it's slow for our purposes 
select 
    id, val 
from t 
where (id, ts) in 
    (select 
    id, 
    max(ts) 
    from t 
    group by id); 

-- analytic ranking 
select 
    id, val 
from 
    (select 
    row_number() over (partition by id order by ts desc) as rank, 
    id, 
    val 
    from t) ranked 
where rank = 1; 

-- distincting analytic 
-- distinct effectively dedupes the rows that end up with same values 
select 
    distinct id, val 
from 
    (select 
    id, 
    first_value(val) over (partition by id order by ts desc) as val 
    from t) ranked; 

Аналитическая классификация запросов чувствует, как тот, для которого это было бы проще всего придумать эффективный план запроса. Но эстетически и технически, это довольно уродливо (особенно, когда таблица имеет более чем один столбец значений). В нескольких местах производства мы используем отчетливый аналитический запрос, когда тестирование показывает, что производительность эквивалентна.

Есть ли способ сделать что-то вроде rank = 1, не закончив таким уродливым запросом?

+0

Какой результат вы ожидаете, если есть еще одна строка типа 'a, 10, 13: 45'? (поэтому есть записи, где комбинация id и val не уникальна). – Beryllium

+0

@Beryllium все три запроса, которые я представляю, должны выбрать последнее значение по метке времени. Поэтому он должен отлично справляться с этим, если ваша строка была добавлена ​​в таблицу образцов. Если для одного и того же значения есть 2 одинаковых временных метки, это может вызвать проблему с агрегированным запросом. – kimbo305

+0

+1 для «костыля»! Distinct является наиболее широко используемым kludge для устранения дубликатов, когда на самом деле есть проблема с запросом. Есть законное использование, но для меня это красный флаг в вопросе, когда я его вижу. – Bohemian

ответ

1

Если группировка по id только

select 
    id, max(ts) 
    from x 
    group by id 
    order by id 

и, если группа состоит из id и val

select 
    id, val, max(ts) 
    from 
    x 
    group by id, val 
    order by id, val 

, так что я не использовал бы поставить агрегат в подзапросе (может быть медленнее) Я бы тоже не использовал функции оконной аггрегации (потому что вы можете сделать это с помощью простых group by и max) , и я бы не использовал distinct, потому что это означает что-то другое (по крайней мере для меня).

Если группа по id, и вы хотите один из значений val, я предлагаю использовать функции окна агрегатные, потому что вы должны определить, каким-то образом , которыеval выбрать: И это намерение входит в order by сразу после partition by.

С точки зрения обслуживания я считаю, что функция агрегации окон действительно описывает ваше намерение - чего вы хотите достичь. Другие запросы скрывают свое намерение. Лично, когда я читал ваши запросы, второй из них был самым простым для понимания.

С точки зрения производительности я могу подтвердить, что агрегаты окон бывают быстрыми (по крайней мере, в моих случаях). Возможно, оптимизатор также использует синтаксис.

1

Это самый простой и быстрый:

select distinct on (id) 
    id, ts, val 
from t 
order by id, ts desc 

distinc on (Postgresql только) будет возвращать только одну строку для каждого идентификатора. С order by вы контролируете, какой из них. В этом случае последние ts. С помощью distinct on вы можете включить в результирующий набор столько столбцов, сколько вам нужно, не требуя промежуточных шагов. Колонка (колонки), используемая в distinct on, должна быть включена сначала в order by.