Получение последних значений из разных столбцов на основе другого столбца

#sql #postgresql

#sql #postgresql

Вопрос:

Итак, у меня есть запрос, подобный этому

 select 
  user_id,
  MAX(
    case when attribute = 'one' then newValue else 'No Update' end
  ) as one,
  MAX(
    case when attribute = 'two' then newValue else 'No Update' end
  ) as two,
  MAX(
    case when attribute = 'thre' then newValue else 'No Update' end
  ) as thre,
from table
group by user_id
  

Этот запрос возвращает результат как максимальное значение для этого конкретного значения атрибута в другом столбце.

В этой таблице также есть updated_at столбец. Теперь вместо этого я хочу, чтобы возвращаемый столбец содержал последнее значение в соответствии с этим полем updated_at.

Таким образом, в основном столбец one, two and thre должен либо содержать последние значения в соответствии с updated_at полем. Если там нет значений, то столбец должен содержать No Update строку.

Что может быть правильным способом?

Пример

 user_id | attribute | newValue | updatedAt
1       | one       | null     | 2018-01-20
1       | one       | b        | 2018-01-21
1       | one       | a        | 2018-01-22
1       | two       | null     | 2018-01-23
1       | two       | null     | 2018-01-24
1       | two       | null     | 2018-01-25
  

Итак, для приведенной выше таблицы текущий запрос вернет результат, поскольку это b является Max значением для attribute=one

 user_id | one | two 
    1   | b   | No Update
  

Но я хочу, чтобы результат столбца one был последним в соответствии с updatedAt таким столбцом

 user_id | one | two 
    1   | a   | No Update
  

Комментарии:

1. Покажите некоторые примеры данных и пример желаемых результатов.

2. Обновлено @MatBailie. Дайте мне знать, если пример неясен.

Ответ №1:

У Postgres нет функций агрегирования first и last, но вы можете получить аналогичную функциональность с помощью массивов:

 select user_id,
       coalesce((array_agg(newvalue order by updatedAt desc) filter (where attribute = 'one'))[1], 'No update') as one,
       coalesce((array_agg(newvalue order by updatedAt desc) filter (where attribute = 'two'))[1], 'No update') as two
from t 
group by user_id;
  

Здесь находится db<>fiddle.

Ответ №2:

Сначала используйте DISTINCT ON (user_id, attribute) , чтобы получить только самые последние строки updatedAt для каждой attribute , а затем агрегируйте результаты:

 select user_id,
       coalesce(max(case when attribute = 'one' then newvalue end), 'No Update') one,
       coalesce(max(case when attribute = 'two' then newvalue end), 'No Update') two
from (
  select distinct on (user_id, attribute) *
  from tablename
  order by user_id, attribute, updatedAt desc
) t 
group by user_id
  

Смотрите демонстрацию.
Результаты:

 > user_id | one | two      
> ------: | :-- | :--------
>       1 | a   | No Update