6

The problem arise when there are no data for books in specific library. Consider a following working scenario.

Table library

--------------------------------
| id | name | owner |
--------------------------------
| 1 | ABC | A |
| 2 | DEF | D |
| 3 | GHI | G |
--------------------------------

Table books

--------------------------------
| id | title | library |
--------------------------------
| a | xxx | 1 |
| b | yyy | 1 |
| c | zzz | 2 |
--------------------------------

Now when I do query like below:

SELECT library.name, array_agg(b.title) AS book_list FROM library, 
(SELECT title FROM books WHERE books.library = :library_no) as b 
WHERE library.id = :library_no GROUP BY library.id

The query generates output for library 1 & 2, but not for library 3. Why and how to solve this issue? (Generate an empty list on no library books)

Required Output:

----------------------
| name | book_list |
----------------------
| GHI | {} | # or {null}
-----------------------

I've even tried coalesce as below:

SELECT library.name, coalesce(array_agg(b.title), ARRAY[]::VARCHAR[]) AS book_list FROM library, 
(SELECT title FROM books WHERE books.library = :library_no) as b 
WHERE library.id = :library_no GROUP BY library.id

Postgres version: 12

Peter Vandivier
5,8311 gold badge25 silver badges50 bronze badges
asked Jan 14, 2021 at 3:01

2 Answers 2

10

A LEFT JOIN can solve it, like Laurenz provided.

But I suggest an ARRAY constructor in a LATERAL subquery instead:

SELECT l.name, b.book_list
FROM library l
CROSS JOIN LATERAL (
 SELECT ARRAY(
 SELECT title
 FROM books
 WHERE library = l.id
 )
 ) b(book_list)
WHERE l.id = :library_no;

This way, you don't need to aggregate in the outer query level and don't need to GROUP BY there.

You also don't need COALESCE, since the ARRAY constructor over an empty result already produces an empty array ({}).

And it should be faster for a small selection in library - obviously the query gets the result for a single given library.

Aside, you only need the variable :library_no in a single place like demonstrated.

About LATERAL joins:

About the ARRAY constructor:

Basic about joining tables in the manual.

answered Jan 14, 2021 at 5:10
1
  • 1
    Basics for LATERAL with links to more: stackoverflow.com/a/28557803/939860. And CROSS JOIN is the simplest way to join. It's an unconditional join: all rows on the left are combined with all rows on the right. Since there is exactly 1 row on the right in the example, that's all we need. Commented Jan 14, 2021 at 5:23
5

You need a left join:

... FROM library
 LEFT JOIN books ON library.id = books.library

Then you will also get libraries with no books.

answered Jan 14, 2021 at 3:48
2
  • Yes this do solve my problem. But shouldn't coalesce produce an empty array when there is no data in books? Commented Jan 14, 2021 at 4:11
  • 4
    @Pax: COALESCE can only step in when there is a row to begin with. Commented Jan 14, 2021 at 4:58

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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.