Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Postgresql, retrieve value for specific key from json array

I have Postgres JSONB array of objects, looking like this :

'[
  {
    "skillId": "1",
    "skillLevel": 42
  },
  {
    "skillId": "2",
    "skillLevel": 41
  }
]'

This JSONB is a function argument.

What is the most efficient way to retrieve skillLevel for skillId = "1".

I've tried to play with jsonb_array_elements but everything I've done so far is looking really messy.

like image 845
Aliaksei Stadnik Avatar asked Jul 06 '18 14:07

Aliaksei Stadnik


People also ask

How do I query JSON data in PostgreSQL?

Querying the JSON document PostgreSQL has two native operators -> and ->> to query JSON documents. The first operator -> returns a JSON object, while the operator ->> returns text. These operators work on both JSON as well as JSONB columns. There are additional operators available for JSONB columns.

How do I check if a JSON key exists in Postgres?

In Postgres, if you select a key that does not exist it will return null. so u can check the existence of a key by checking the null value of that key.

What is Jsonb in Postgres?

JSONB stands for “JSON Binary” or “JSON better” depending on whom you ask. It is a decomposed binary format to store JSON. JSONB supports indexing the JSON data, and is very efficient at parsing and querying the JSON data. In most cases, when you work with JSON in PostgreSQL, you should be using JSONB.


1 Answers

In Postgres 9.4+ use the function jsonb_array_elements() in a lateral join:

select (elem->>'skillLevel')::int as skill_level
from my_table
cross join jsonb_array_elements(json_col) elem
where elem->>'skillId' = '1';

You can implement the idea in a simple function, e.g:

create or replace function extract_skill_level(json_data jsonb, id int)
returns integer language sql as $$
    select (elem->>'skillLevel')::int
    from jsonb_array_elements(json_data) elem
    where elem->>'skillId' = id::text
$$;

select extract_skill_level(json_col, 1) as skill_level
from my_table;

In Postgres 12+ you have a nice alternative in the form of jsonb path functions:

select (
    jsonb_path_query(
        json_col, 
        '$[*] ? (@.skillId == "1")'
        )->'skillLevel'
    )::int as skill_level
from my_table;

Db<>Fiddle.

Read more about JSON Functions and Operators.

like image 54
klin Avatar answered Oct 17 '22 09:10

klin