JSONB Aggregation (jsonb_agg, json_build_object) in SQL
`jsonb_agg` and `json_build_object` move in opposite directions across the boundary between relational and JSONB structure. `jsonb_agg` collects relational rows into a JSONB array. `json_build_object` assembles a JSONB object from key-value pairs supplied as arguments. Together they cover the two primary operations analysts need when producing JSONB output from relational data.
Before this JSONB Field Extraction, GROUP BY
jsonb_agg and json_build_object turn relational rows into JSON structure. They're what you reach for when a query needs to deliver nested data instead of the flat multi-row output SQL normally produces.
A common scenario: your API endpoint expects one JSON object per category, with all products in that category embedded as an array. Fetching categories and products with a JOIN gives you one row per product — one row per category is what the API needs. These two functions collapse that into one row per category with a structured JSON array of products.
The two functions work together. json_build_object packages multiple columns from a single row into a JSON object. jsonb_agg then collects those objects across rows into a JSON array, one object per row.
The GROUP BY collapses all products for each category into one output row. json_build_object converts each product row into a JSON object with two keys: name and price. jsonb_agg collects those objects into an array. The ORDER BY inside jsonb_agg controls the sequence of elements within each array — independent of any ORDER BY at the query level.
json_build_object takes alternating key-value arguments: key, value, key, value. Keys must be text. Values can be any type that has a JSON representation — integers become JSON numbers, text becomes JSON strings, NULL becomes JSON null.
jsonb_build_object vs json_build_object
The difference is the return type: json_build_object returns json, jsonb_build_object returns jsonb. For storing results in a jsonb column or passing them to other JSONB functions, use the jsonb_ variant. For most output purposes, both behave identically.
The one thing that trips people up
NULL handling differs between the aggregate call and the object construction. If the entire expression passed to jsonb_agg evaluates to NULL, that row is excluded from the array — same as any aggregate ignoring NULLs. But if a specific field inside json_build_object is NULL, the key is still included in the resulting object with a JSON null value. The row itself is not excluded.
When you only want some rows to appear in the array, use FILTER:
jsonb_agg(json_build_object('id', oi.product_id) FILTER (WHERE oi.quantity > 0))This is most useful in the final shaping step of a query — after all joins and group-bys are done and the task is packing the results into a format a downstream API or pipeline expects.
jsonb_agg vs STRING_AGG
If the goal is a display-ready list of values from one column, STRING_AGG is simpler: STRING_AGG(product_name, ', ') gives you a comma-separated string directly. Use jsonb_agg with json_build_object when you need to capture multiple fields per row, preserve the structure for downstream processing, or produce output that an API or pipeline expects to parse as JSON. The moment you need more than one field per collected row, the JSON functions are the right tool.
10 JSONB Aggregation (jsonb_agg, json_build_object) practice problems
Write a query to return every category ID alongside a JSON array of product names for that category.
Write a query to return a JSON object for product `id = 7` containing the product's ID, name, and price under the keys `'id'`, `'name'`, and `'price'`.
Write a query to return every category ID and a JSON array of objects — one per product — where each object contains the keys `'id'` and `'name'` for that product.
Write a query to return every category ID, the total number of products in that category as `product_count`, and a JSON array of those product names as `product_names`.
Write a query to return a JSON object for every product whose `'format'` attribute is `'Paperback'`. The object must have keys `'id'`, `'name'`, and `'pages'`, where `'pages'` is the value extracted from the product's `attributes` under the `'pages'` key.
Write a query to return one JSON array collecting every event whose `event_type` is `'page_view'`. Each array element is a JSON object with keys `'id'` (the event ID) and `'page'` (the page path from the event's `properties`).
Write a query to return every category ID, the total `price` of qualifying products in that category as `total_price`, and a JSON array of those product names as `product_names`.
Write a query to return every category ID and a JSON array of `'color'` attribute values for products in that category, pulling each color from the product's `attributes`.
Write a query to return every category ID and a JSON array of objects — one per product in that category — where each object contains the product's `id` under the key `'id'` and the product's color attribute under the key `'color'`.
Write a query to return a JSON object for product `id = 9` with the keys `'name'` (the product name) and `'warranty_years'` (the warranty term as a JSON number).
These problems are part of the JSONB Aggregation (jsonb_agg, json_build_object) lesson in SQLMaxx, with instant grading and a worked solution on each.
Reading explains SQL. Writing it, over and over with instant feedback, is what makes you fluent.
That's the whole SQLMaxx loop: 600+ real problems, instant AI feedback, mastery you can actually see, and spaced review that won't let you forget.
Real problems. Not toy examples.
615 hand-built problems spanning all 66 concepts, from basic SELECTs to window functions, built on real schemas and real business questions, the kind you'll actually get asked on the job. Enough reps to make SQL automatic.
Write a query. Know if it's right in one second.
No copying an answer and hoping it clicked. The AI grader checks your real query against real data, catches exactly what's wrong, and explains the fix in plain English, like a senior analyst reading over your shoulder on every problem.
Stop guessing whether you actually know it.
SQLMaxx tracks every concept and shows you what you've mastered and what's still shaky. Your skills fill in one concept at a time, so 'I think I get joins' becomes something you can prove.
Learn it once. Keep it for good.
Most of what you learn this week fades by next week. So when a concept comes due for review, SQLMaxx hands you a fresh problem to solve from a blank editor, not a flashcard to re-read. A research-backed spaced-repetition algorithm (FSRS) times each return for right before you'd forget, so your SQL is still there months later, when the interview or the job actually needs it.
Practice, feedback, mastery, review. That's the loop that turns reading into real skill.
Start freeNo account, no credit card. Start solving in under a minute.