0

I have 2 tables a and b

Table a

id          |   name        | code
VARCHAR         VARCHAR       jsonb
1               xyz          [14, 15, 16 ]
2               abc          [null]
3               def          [null]

Table b

id | name | code
1    xyz    [16, 15, 14 ]
2    abc        [null]

I want to figure out where the code does not match for same id and name. I sort code column in b b/c i know it same but sorted differently

SELECT                  a.id,
                        a.name,
                        a.code,
                        c.id,
                        c.name,
                        c.code
                        
                        FROM                a
                        FULL OUTER JOIN     (   SELECT          id,
                                                                name,
                                                                jsonb_agg(code ORDER BY code) AS code
                                                FROM        (
                                                                SELECT          id,
                                                                                name,
                                                                                jsonb_array_elements(code) AS code
                                                                FROM            b
                                                                GROUP BY        id,
                                                                                name,
                                                                                jsonb_array_elements(code)
                                                            ) t
                                                GROUP BY        id,
                                                                name
                                            ) c
                            ON              a.id = c.id
                            AND             a.name = c.name
                            AND             COALESCE (a.code, '[]'::jsonb) = COALESCE (c.code, '[]'::jsonb)
                        WHERE               (a.id IS NULL OR c.id IS NULL)

My answer in this case should only return id = 3 b/c its not in b table but my query is returning id = 2 as well b/c i am not handling the null case well enough in the inner subquery How can i handle the null use case in the inner subquery?

1
  • 1) what exactly do you mean when you say that two jsonb "match"? 2) It took me a while to figure out that you mean "because" when you write "b/c". Not smart when you are writing about tables called a and b. 3) The code is unreadable. Could you remove most of the spaces? Commented Jul 19, 2019 at 18:54

1 Answer 1

2

demo:db<>fiddle

The <@ operator checks if all elements of the left array occur in the right one. The @> does other way round. So using both you can ensure that both arrays contain the same elements:

a.code @> b.code AND a.code <@ b.code

Nevertheless it will be accept as well if one array contains duplicates. So [42,42] will be the same as [42]. If you want to avoid this as well you should check the array length as well

AND jsonb_array_length(a.code) = jsonb_array_length(b.code)

Furthermore you might check if both values are NULL. This case has to be checked separately:

a.code IS NULL and b.code IS NULL

A little bit shorter form is using the COALESCE function:

COALESCE(a.code, b.code) IS NULL

So the whole query could look like this:

SELECT 
    *
FROM a
FULL OUTER JOIN b 
ON a.id = b.id AND a.name = b.name
AND (
    COALESCE(a.code, b.code) IS NULL            -- both null
    OR (a.code @> b.code AND a.code <@ b.code 
        AND jsonb_array_length(a.code) = jsonb_array_length(b.code) -- avoid accepting duplicates
    )
)

After that you are able to filter the NULL values in the WHERE clause

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

1 Comment

This answer worked like a charm and i got to learn <@ operator. Thanks for taking the time to explain in detail your solution.

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.