Search code examples
postgresqljsonb

How to reference a jsonb coulmn value from a value map in postgres


I want to be able to reference the error table for msg and description based on the err_id on the results table for err_map jsonb column, I'd also want to be able relate which error occurred against which column whether the independent columns c1,c2 or val_map jsonb column c3, c4)

the only reason the val_map stores data(with .) as "val_map.c3": 3 so we can identify that these columns were from val_map when mapping errors to columns.

I have a result table here the err_map column values 1,3 reference to below error table

   id    |   c1    |  c2     | val_map              | err_map
----------------------------------------------------------------
    1    |   chk1  |  chk2   | {"c3":3, "c4":4}     | {"c1": 1, "val_map.c3": 3}

Error Table

   id    |   msg   |  description
----------------------------------------------------------------
    1    |   msg1  |  an error1 occurred
----------------------------------------------------------------
    3    |   msg3  |  an error3 occurred

I looked at jsonb_each and jsonb_object_keys but can't really figure out how to use it to join these tables. Any help/hints will be appreciated. Pardon if something is unclear, please ask to provide more detail.

[Edit 1]: removed foreign key reference as it was misleading

[Edit 2]: I've got it working but it's quite inefficient

select
  e.error_key,
  e.error_message,
  T2.key as key
from result.error e
inner join (
  select
    substring(T1.key, 11) as key,
    T1.value
  from (
    select em.key, em.value
    from result rd, jsonb_each(rd.error_map) as em
  ) as T1
  where T1.key like '%value_map%'
union all
  select T1.key , T1.value
  from (
    select em.key, em.value
    from result rd, jsonb_each(rd.error_map) as em
  ) as T1
  where T1.key not like '%value_map%'
) as T2 on T2.value::bigint = e.id; 

Solution

  • You can simplify that UNION ALL to just

    select
      e.error_key,
      e.error_message,
      T2.key as key
    from result.error e
    inner join (
      select
        case when T1.key like 'val_map.%'
          then substring(T1.key, 9)
          else T1.key
        end as key,
        T1.value
      from result rd, jsonb_each(rd.error_map) as T1
    ) as T2 on T2.value::bigint = e.id;