megafax
@megafax
web-программист

Как правильно составить запрос для взятия первого значения из группы?

Есть таблица

timestamp                        | data
-----------------------------------------------------------------
2017-12-10 10:33:19.563241+00    | {"foo":900}
2017-12-11 10:34:19.563241+00    | {"foo":1800}
2017-12-11 10:35:19.563241+00    | {"foo":1800,"bar":400}
2017-12-11 10:36:19.563241+00    | {"foo":2400,"bar":400}


Из нее надо взять последнее значение столбца data для каждого дня, т.е. чтобы получилось

day                       | data
--------------------------------------------------------
2017-12-11 00:00:00+00    | {"foo":2400,"bar":400}
2017-12-10 00:00:00+00    | {"foo":900}


Понимаю, что нужно использовать оконные функции, но для типа json нет функции сравнения
т.е для запроса вида
SELECT DISTINCT "timestamp", date_trunc('day', "timestamp") AS "day", FIRST_VALUE("data") OVER (PARTITION BY "timestamp")
FROM "table"
ORDER BY "timestamp" DESC

получаю ошибку не удалось найти оператор равенства для типа json
Как такое все таки решить?
Система: PostgreSQL 9.5 Win 7
  • Вопрос задан
  • 848 просмотров
Решения вопроса 2
Melkij
@Melkij
PostgreSQL DBA
Ну а когда станет скучно перебирать всю таблицу - обращайтесь :-)

Postgresql 9.4 и выше, выбор по известному диапазону дат через index scan по timestamp полю:
select day, data 
from generate_series('2017-12-10', '2017-12-12', interval '1 day') as day, 
lateral (
    select data from tablename 
    where "timestamp" between day and day + interval '1 day' 
    order by "timestamp" desc limit 1
) ljd;


Все дни из таблицы по индексу:
with recursive t as (
(select "timestamp"::date as day, data from tablename order by "timestamp" desc limit 1)
union all
select bpt.* from t, lateral (
select "timestamp"::date as day, data from tablename where "timestamp" < t.day order by "timestamp" desc limit 1
) as bpt
)
select * from t;

Используя loose index scan
Ответ написан
@azio
Без дополнительных условий задачи можно так
SELECT
  date_trunc('day', sub."timestamp") AS "day",
  sub."data"
FROM (SELECT
        "timestamp",
        "data",
        row_number() OVER ( PARTITION BY date_trunc('day', "timestamp")
          ORDER BY "timestamp" DESC ) AS rownum
      FROM "table") sub
WHERE sub.rownum = 1


Только следует помнить, что этот вариант переберет все записи в таблице, поэтому может оказаться неприменимым для больших таблиц
Ответ написан
Пригласить эксперта
Ответы на вопрос 2
megafax
@megafax Автор вопроса
web-программист
Как одно из решений - это преобразование json в jsonb и с ним уже работать
Если есть другие варианты - было бы неплохо
Ответ написан
Комментировать
@d-stream
Готовые решения - не подаю, но...
как извращенный, но рабочий вариант:

select 
*
from table 
inner join (
select 
dt=max(timestamp) 
from table
group by date_trunc('day', "timestamp")  -- под рукой mssql - там юзал cast(... as date)
) as t on t.dt=table.timestamp
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы