0

Assume I have the following key-value object:

[{"Gin" : "Au"}, {"Beer": "US"}]

Now, I would like to perform the following queries:

UPDATE product SET location = "AU" WHERE name = "Gin";

UPDATE product SET location = "Beer" WHERE name = "US";

Is it possible to send a key-value object to postgres so it will do the mapping on its own? I want to wrap everything in one request to the database.

2
  • You should avoid trying to store unnormalized data in your database. The way I would approach this is to iterate over the JSON object and fire UPDATE commands as you have done above. Nothing wrong with that. Commented Aug 7, 2015 at 7:03
  • Thank you for your feedback @Tim. In case I will have a hundred keys in my json, that would fire 100 requests to the database, wouldn't that be bad practise? Thank you Commented Aug 7, 2015 at 7:13

1 Answer 1

1

You essentially need to convert the JSON array you have into a set of rows that you can join to the product table:

Doing this is a bit complicated - at least I couldn't find a better way to do it:

with elements (name, map) as (
  select json_object_keys(map::json) as name, map::json
  from json_array_elements_text('[{"Gin" : "Au"}, {"Beer": "US"}]') as map
)
select name, trim(both '"' from (map -> name)::text) as location
from elements;

I have the feeling there should be a more elegant solution to turn the JSON into a relational set, but I can't figure it out right now.

This will return this:

name | location
-----+---------
Gin  | Au      
Beer | US      

Now this result can be used to update the product table:

with elements (name, map) as (
  select json_object_keys(map::json) as name, map::json
  from json_array_elements_text('[{"Gin" : "Au"}, {"Beer": "US"}]') as map
)
update product 
  set location = r.location
from (
  select name, trim(both '"' from (map -> name)::text) as location
  from elements
) as r (name, location)
  where product.name = r.name;

As you are going to do this a lot, I suggest to write a function that turns your JSON into a proper relational result:

create function get_products(p_mapping text)
  returns table (name text, location text)
as 
$$
  with elements (name, map) as (
    select json_object_keys(map::json) as name, map::json
    from json_array_elements_text(p_mapping::json) as map
  )
  select name, trim(both '"' from (map -> name)::text) as location
  from elements;
$$
language sql;

Then the update looks quite simple:

update product 
  set location = r.location
from get_products('[{"Gin" : "Au"}, {"Beer": "US"}]') r
  where product.name = r.name;
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you @horse for your answer

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.