Я не знаю ни для MySQL, ни для MongoDB, но вот простая версия для PostgreSQL JSONB типа:
SELECT (doc->'yearly'-> max(years) -> 'ebit')::numeric AS ebit
FROM reports, jsonb_object_keys(doc->'yearly') AS years
GROUP BY reports.doc;
... с упрощенными данными испытаний:
WITH reports(doc) AS (
SELECT '{"yearly":{"2018-12-31":{"ebit":123},"2017-12-31":{"ebit":1.23}}}'::jsonb
)
SELECT (doc->'yearly'-> max(years) -> 'ebit')::numeric AS ebit
FROM reports, jsonb_object_keys(doc->'yearly') AS years
GROUP BY reports.doc;
... дает:
ebit
------
123
(1 row)
Итак, я в основном выбрал последний в разделе "yearly"
, не зная фактических значений, но предполагая, что форматирование контрольной даты разрешит порядок сортировки (в этом случае, похоже, он соответствует ISO-8601 ).
Использование типа данных JSON
вместо JSONB
сохранит порядок ключей объекта, но не столь эффективен в PostgreSQL в дальнейшем и здесь не поможет.
Если вы хотите затем выбрать только те записи reports
, у которых их последние ebit
больше определенного значения, просто упакуйте их во вспомогательный выбор или CTE. Я обычно предпочитаю CTE, потому что они лучше читаются, поэтому мы идем:
WITH
reports (id, doc) AS (
VALUES
(1, '{"yearly":{"2018-12-31":{"ebit":123},"2017-12-31":{"ebit":1.23}}}'::jsonb),
(2, '{"yearly":{"2018-12-23":{"ebit":50},"2017-12-22":{"ebit":"1200.00"}}}'::jsonb)
),
r_ebit (id, ebit) AS (
SELECT reports.id, (reports.doc->'yearly'-> max(years) -> 'ebit')::numeric AS ebit
FROM reports, jsonb_object_keys(doc->'yearly') AS years
GROUP BY reports.id, reports.doc
)
SELECT id, ebit
FROM r_ebit
WHERE ebit > 100;
Однако, как вы уже видели, с помощью этой стратегии невозможно отфильтровать исходные строки. Шаг предварительной обработки имеет смысл, так что формат JSON на самом деле удобен для фильтров.
ДОПОЛНЕНИЕ
Чтобы добавить возможность выбора значений для n -го завершенного финансового года, нам нужно прибегнуть к оконным функциям, а также нам нужно уменьшить результирующий набор, чтобы он возвращал только одну строку для фактической группы (в демонстрационном случае: reports.id
):
WITH reports(id, doc) AS (VALUES
(1, '{"yearly":{"2018-12-31":{"ebit":123},"2017-12-31":{"ebit":1.23},"2016-12-31":{"ebit":"23.42"}}}'::jsonb),
(2, '{"yearly":{"2018-12-23":{"ebit":50},"2017-12-22":{"ebit":"1200.00"}}}'::jsonb)
)
SELECT DISTINCT ON (1) reports.id, (reports.doc->'yearly'-> (lead(years, 0) over (partition by reports.doc order by years desc nulls last)) ->>'ebit')::numeric AS ebit
FROM reports, jsonb_object_keys(doc->'yearly') AS years
GROUP BY 1, reports.doc, years.years ORDER BY 1;
... будет вести себя точно так же, как при использовании агрегатной функции max
ранее. При увеличении параметра смещения в функции lead(years, <offset>)
все выберет n -й год назад (в порядке убывания раздела окна).
Предложение DISTINCT ON (1)
- это волшебство, которое сводит результат к одной строке на отдельное значение столбца (первый столбец = reports.id
). Вот почему NULLS LAST
очень важно в предложении window OVER
.
Вот результаты для разных смещений (я добавил третью историческую запись для первой id
, но не для второй, чтобы также показать, как она работает с отсутствующими записями):
N = 0 :
id | ebit
----+------
1 | 123
2 | 50
N = 1
id | ebit
----+---------
1 | 1.23
2 | 1200.00
N = 2
id | ebit
----+-------
1 | 23.42
2 |
... что означает, что отсутствующие записи просто приведут к значению NULL
.