0

I have a table structured like:

CREATE TABLE artists (artist TEXT UNIQUE, facts JSONB);

INSERT INTO artists (artist, facts) 
  VALUES ('adele', '[{"type": "full_name", "value": "Adele Laurie"}, {"type": "age", "value": "25"}]');

INSERT INTO artists (artist, facts) 
  VALUES ('taylor', '[{"type": "age", "value": "25"}, {"type": "last_album", "value": "1989"}]');

There are a fixed number of fact "type"s, but not every artist will have each fact. How can I select a result with columns for each fact type and null's for missing fact names?

Desired output:

| artist |  full_name    | age  | last_album |
|--------|---------------|------|------------|
| adele  | Adele Laurie  | 25   | null       |
| taylor | null          | 25   | 1989       |
6
  • Where the table tracks came from ? Commented Nov 24, 2015 at 19:23
  • Whoops, bad copy paste, fixed now Commented Nov 24, 2015 at 19:29
  • 1
    The first row has a defined object {"type": "full_name", "value": "Adele Laurie"} while the second doesn't {"age": "25"} is the second pattern right OR is there missing also a type, like {"type": "age", "value": "25"} Commented Nov 24, 2015 at 20:43
  • Ok, get it! this is the problem... Commented Nov 24, 2015 at 20:46
  • Oh shoot, you're right again. Sorry for the typos. Yes, the json is the same structure in both rows (with type and value keys for both). I fixed it. Commented Nov 24, 2015 at 20:55

2 Answers 2

1

You can do as this:

select a.artist, 
     max(case when b.value->>'type' = 'full_name' 
         then b.value->>'value' 
         else b.value->>'full_name' end) as full_name,
     max(case when b.value->>'type' = 'age' 
         then b.value->>'value' 
         else b.value->>'age' end) as age,
     max(case when b.value->>'type' = 'last_album' 
         then b.value->>'value' 
         else b.value->>'last_album' end) as last_album
from artists a, 
     json_array_elements(a.facts) b
group by a.artist
order by a.artist

See it here: http://sqlfiddle.com/#!15/e376b/2

In the fiddle I created the field as JSON since there is not available the JSONB type

If you need to add more types, just add it as a case condition like the others. I think you can figure it out from here :)

EDIT

Even with your change in the format this query should solve your problem. Just edited the fiddle. See it here: http://sqlfiddle.com/#!15/1c2b6/2

The only difference is that you don't really need the else for the case statement.

This is the query without the else statements

select a.artist, 
     max(case when b.value->>'type' = 'full_name' 
         then b.value->>'value' end) as full_name,
     max(case when b.value->>'type' = 'age' 
         then b.value->>'value' end) as age,
     max(case when b.value->>'type' = 'last_album' 
         then b.value->>'value' end) as last_album
from artists2 a, 
     json_array_elements(a.facts) b
group by a.artist
order by a.artist;

I edited the SqlFiddle link up here.

Sign up to request clarification or add additional context in comments.

2 Comments

That works great! I'm having trouble viewing the sqlfiddle's though. Can you fix the links and edit to just answer the question as is?
I've edited it with the query. And the sqlfiddle links are working to me here. It sometimes stop working, lately it is unstable. Try again.
0

I would go with crosstab() function. @Eriwn has really good answers with using this approach.

Example:

SELECT * FROM crosstab(
    'WITH data AS (
      SELECT artist,fact
      FROM artists a, jsonb_array_elements(a.facts) fact
    )
    SELECT artist,type,value
    FROM data,jsonb_to_record(data.fact) AS x(type text,value text)',
    'WITH data AS (
      SELECT artist,fact
      FROM artists a, jsonb_array_elements(a.facts) fact
     )
    SELECT DISTINCT type
   FROM data,jsonb_to_record(data.fact) AS x(type text,value text)'
) AS ct (artist text,age text,full_name text,last_album TEXT);

Result:

 artist | age  |  full_name   | last_album 
--------+------+--------------+------------
 adele  |      | Adele Laurie | 25
 taylor | 1989 |              | 25
(2 rows)

2 Comments

You are using four select statements for doing something that needs only one. In a large data sets this would be an overkill. Always use K.I.S.S. methodology :)
Yeah, with crosstab there is no way you can reuse actual text SQL query, unless you show the one. The drawback to use 2 selects, but with adding more attributes into Json SQL should not be changed greatly @JorgeCampos

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.