Как получить из вложенного JSON по int, а не по имени в MySQL 8 - PullRequest
0 голосов
/ 22 апреля 2019

Так что в настоящее время я использую поле JSON MySQL для хранения некоторых данных.

Таким образом, таблица «отчетов» выглядит следующим образом:

id | stock_id | type             | doc                          |
1  | 5        | Income_Statement | https://pastebin.com/bj1hdK0S|

Pasbin - это содержимое поля json

То, что я хочу сделать, это получить число (ebit) из первого объекта в годовой (2018-12-31) в JSON, а затем использовать его для выполнения запроса WHERE, чтобы он возвращал только там, где ebit> 50000000 например. Проблема в том, что даты в годовом исчислении не являются стандартными (то есть один может быть 2018-12-31, другой может быть 2018-12-15). По сути, я хочу получить данные, используя целочисленные индексы, а не фактические имена объектов, что-то вроде year. [0] .ebit.

Как бы я сделал это в MySQL? В качестве альтернативы, если это невозможно в MySQL, возможно ли это в PostgeSQL или Mongo? Если да, не могли бы вы привести пример? Большая часть данных хорошо вписывается в MySQL, только в этой таблице есть столбец JSON, поэтому я начал с MySQL.

так что StackOverflow не позволяет моей ссылке на pastebin без некоторого кода, так что вот некоторый случайный код:

if(dog == "poodle") {
    print "test"
}

1 Ответ

1 голос
/ 22 апреля 2019

Я не знаю ни для 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.

...