Нумерация строк в Postgresql внутри функции агрегации

Как пронумеровать строки после работы функции json_agg или же как обойти эту конструкцию?

Что нужно сделать?

ВАЖНО: Не прошу сделать за меня. Просто подать идею.

Само, скажем так, ТЗ звучит следующим образом: "Нужно собрать все ошибки, которые относятся к одному разделу, в какую-то итерируемую форму, по которой можно пройтись и вывести в цикле все ошибки, которые принадлежат определенному разделу, при этом отдельно должны быть пронумерованы записи по порядковому номеру раздела и отдельно должны быть пронумерованы ошибки по порядковому номеру ошибки в рамках раздела"

Итерируемая форма нужна для того, чтобы эту инфу смог вытащить шаблонизатор в шаблон для docx документа.

Сейчас у меня получилось сделать так (запрос к базе отдает json, все верно):

[
  {
    "num": "1",
    "error_razdel": "Error_1",
    "ident": "actual",
    "field_values":[
      {
        "NNN": null,
        "error": "some_txt_error"
      }
    ]
  },
  {
    "num": "2",
    "error_razdel": "Error_2",
    "ident": "not actual",
    "field_values":[
      {
        "NNN": 1,
        "error": "no output"
      }
    ]
  },
  {
    "num": "3",
    "error_razdel": "Error_3",
    "ident": "research",
    "field_values":[
      {
        "NNN": null,
        "error": "some_txt_error_2"
      },
      {
        "NNN": null,
        "error": "some_txt_error_3"
      },
      {
        "NNN": 1,
        "error": "some_txt_error_4"
      },
    ]
  }
]

А хочу сделать так:

[
      {
        "num": "1",
        "error_name": "Error_1",
        "ident": "actual",
        "field_values":[
          {
            "nums": "1",
            "NNN": null,
            "error": "some_txt_error"
          }
        ]
      },
      {
        "num": "2",
        "error_name": "Error_2",
        "ident": "not actual",
        "field_values":[
          {
            "nums": null,
            "NNN": 1,
            "error": "no output"
          }
        ]
      },
      {
        "num": "3",
        "error_name": "Error_3",
        "ident": "research",
        "field_values":[
          {
            "nums": "1",
            "NNN": null,
            "error": "some_txt_error_2"
          },
          {
            "nums": "2",
            "NNN": null,
            "error": "some_txt_error_3"
          },
          {
            "nums": "3",
            "NNN": 1,
            "error": "some_txt_error_4"
          },
        ]
      }
]

Умышленно не упомянул, что еще желательно бы проверить значение "ident" на актуальность. Все ошибки кроме тех, где "ident" = not actual должны нумероваться, но в целом это бантики и возможно я как-то смогу обработать эту ситуацию силами шаблонизатора (Carbone.io)

В чем проблема и что пытался сделать?

Теперь подробнее о проблеме и о том что пытался сделать: Проблема в том, что по сути мне как-то надо пронумеровать строки в функции агрегации или же переписать запрос так, чтобы данные формировались без функции агрегации или нумеровались, например в CTE. Ни в том ни в другом случае не знаю что делать. В ф-ии агрегации особо ничего не сделаешь, а даже если переписывать запрос, то все равно моя логика сводится к использованию ф-ии агрегации.

Также нужно учитывать, что нельзя использовать view, какие-либо mutation запросы, переменные. Также json нельзя куда-то передать промежуточно. Он сразу передается в вшитый под капот шаблонизатор и никаких инструментов кроме SQL запросов и инструментов самого шаблонизатора нет.

Да в целом кроме SELECT'ов и CTE мало что можно использовать. По сути нужно сформировать готовые данные для шаблонизатора одним запросом, поэтому места для маневра не очень много. Кое как собрал то что собрал, чтобы шаблонизатор смог правильно все вычитать, но нумерация ошибок встала поперек всего этого дела. Смотрел разные кейсы нумерации, но чего-то подходящего мне не нашел.

Первое что пришло в голову - использовать оконную функцию ROW_NUMBER(), однако очень скоро выяснил что использовать оконные функции в функциях агрегации нельзя.

Следующая моя догадка - нужно как то переписать весь запрос, возможно, агрегируя и нумеруя ошибки отдельно, но тут проблема в логике. По сути форму, когда ошибки относятся к конкретному разделу мы получаем в самом конце и какой-то вариант агрегации в CTE тоже отпадает (Хотя ГПТ предлагает именно такой вариант, но он в упор не понимает суть задачи). Пробовал делать через генерацию массивов и другие изощренные способы. Вообщем, попробовав свои догадки, поискав инфу или подобные кейсы в интернете, я что-то подобное не нашел.

Что есть сейчас?

Запрос

select ROW_NUMBER() OVER () as num,
       A.error_name,
       A.ident,
       json_agg(CASE
           WHEN A.ident = 'not actual' THEN
           jsonb_build_object(
           'error', 'no output',
           'NMD',V.NMD)
           ELSE jsonb_build_object(
           'error', V.error,
           'NMD', V.NMD) END ) as field_value
from
(
(
select distinct crfv. as rid, cs.name as ident, cr.name as error_name
from c_req cr
inner join c_req_fv crfv on crfv. = cr.id
right join c_sc_item cs on crfv.val_scale = cs.id
where crfv.ev_id = 10
) as A

left join 
(
select B.error_name as error, B.error_id as rem_id, B.ridd as , K.nmd as nmd
from
(
select distinct cr.name as error_name, cr.id as error_id, crfv.req_id as ridd, cr.id as cid
from cm_error cr
inner join
c_err_req crr on crr.error_id = cr.id
inner join
c_req_fv crfv on crfv.req_id = crr.req_id
where crfv.ev_id = 10
) as B 

right join 

(
select error_id
from c_err_asst cra
inner join c_ev_asst cea on cea.asst_id = cra.asset_id 
where cea.ev_id = 10
union
select error_id
from c_err_dev crd
inner join c_ev_asst cea on cea.asst_id = crd.dev_id
where cea.ev_id = 10
union
select error_id
from c_err_off cro
inner join c_ev_asst cea on cea.asst_id = cro.off_id
where cea.ev_id = 10
union
select error_id
from c_err_zo crz
inner join c_ev_asst cea on cea.asst_id = crz.zo_id 
where cea.ev_id = 10
union
select error_id
from c_err_procc crp
inner join c_ev_asst cea on cea.asst_id = crp.procc_id
where cea.ev_id = 10 
) as C on C.error_id = B.cid

left join 

(
select value_text as nmd, error_id
from cm_err_fv
where field_id = 19
) as K on K.error_id = C.error_id
) as V on A.rid = V. ) group by A.error_name, A.ident

Краткое объяснение по коду: В первом SELECT получаем все объекты наблюдения, а также ident и раздел ошибок для них В следующем большом SELECT получаем все ошибки которые относятся к нашим объектам наблюдения и потом делаем right join, чтобы оставить только те ошибки, которые относятся к наши объектам наблюдения в конкретном предприятии (одни и те же объекты есть на разных предприятиях). Присоединяем поле NMD по id ошибки.

Этот запрос выдает как раз тот результат, который я получил выше.

Буду благодарен за любые подсказки или критику по поводу качества описания вопроса


Ответы (0 шт):