9

I'm wondering if it's possible to do a left outer join between a json_array_elements of a table column and another table? Something like the following, but this doesn't work.

SELECT *
FROM foo,
    json_array_elements (foo.bars :: json) foo_bars
LEFT OUTER JOIN bar ON (foo_bars ->> 'id') :: BIGINT = bar.ID;

The table structure is like the following

FOO
 ------------------------------------------
|  ID  | NAME |            BARS            |
|------------------------------------------|
|  1   | FOO1 | [{ "id" : 1}, { "id" : 2 }]|
|------------------------------------------|
|  2   | FOO1 |             []             |
 ------------------------------------------

BAR
 -------------
|  ID  | NAME |
|-------------|
|  1   | BAR1 |
|-------------|
|  2   | BAR2 |
 -------------

I would expect the output of the query to be

 --------------------------------------------------------
|  ID  | NAME |            BARS            |  ID  | NAME |
|------------------------------------------|-------------|
|  1   | FOO1 | [{ "id" : 1}, { "id" : 2 }]|  1   | BAR1 |
|------------------------------------------|-------------|
|  1   | FOO1 | [{ "id" : 1}, { "id" : 2 }]|  2   | BAR2 |
|------------------------------------------|-------------|
|  2   | FOO1 |             []             | null | null |
 --------------------------------------------------------
1
  • 1
    "doesn't work." is not really a good description of query behaviour vs. desired behaviour. Commented Jun 3, 2015 at 20:12

1 Answer 1

23

To answer your question: Yes it is possible and your query does exactly that. We can prove it by introducing a third row in foo table: http://sqlfiddle.com/#!15/06dfe/2

Your problem is not with LEFT JOIN to json_array_elements but with implicit lateral cross join. Your query is equivalent to:

SELECT *
FROM foo 
  CROSS JOIN LATERAL json_array_elements (foo.bars :: json) foo_bars 
  LEFT OUTER JOIN bar ON (foo_bars ->> 'id') :: BIGINT = bar.ID;

http://sqlfiddle.com/#!15/06dfe/5

What you want is a lateral left join between foo and json_array_elements:

SELECT *
FROM foo LEFT JOIN LATERAL
    json_array_elements (foo.bars :: json) foo_bars ON true
LEFT OUTER JOIN bar ON (foo_bars ->> 'id') :: BIGINT = bar.ID;

http://sqlfiddle.com/#!15/06dfe/6

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

6 Comments

This works great. Even works with null json as well. I'd never heard of SQL Fiddle before either. I wish I could give you 100 upvotes for that one.
I remove the LATERAL and the result is the same.
@鄭元傑 it's implicit probably, it was years ago and I don't understand it anymore.
@JakubKania I am still trying to understand LATERAL. OTZ
|

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.