Home Database SQL Arrays, JSON, and Advanced Aggregation Functions: A Deep Dive
Advanced 5 min · July 13, 2026

SQL Arrays, JSON, and Advanced Aggregation Functions: A Deep Dive

Master SQL arrays, JSON operations, and advanced aggregation functions with production-ready examples.

N
Naren Founder & Principal Engineer

20+ years shipping high-throughput database systems. Lessons pulled from things that broke in production.

Follow
Production
production tested
July 13, 2026
last updated
2,165
articles · all by Naren
Before you start⏱ 20-25 min read
  • Basic understanding of SQL SELECT, GROUP BY, and aggregate functions.
  • Familiarity with PostgreSQL (or similar database with JSON support).
  • Knowledge of basic data types and table creation.
 ● Production Incident 🔎 Debug Guide ⚙ Triage Commands
Quick Answer
  • SQL arrays store ordered collections of elements; use array_agg() to create them from query results.
  • JSON functions like jsonb_extract_path_text() allow querying nested JSON data.
  • Advanced aggregations include FILTER, ORDER BY within aggregates, and ordered-set aggregates.
  • Use jsonb_agg() and jsonb_object_agg() to aggregate rows into JSON arrays and objects.
  • Performance tips: index JSONB columns with GIN indexes; avoid excessive unnesting.
✦ Definition~90s read
What is SQL Arrays, JSON, and Advanced Aggregation Functions?

SQL arrays, JSON, and advanced aggregation functions are features in PostgreSQL that allow you to store and query complex data structures like arrays and JSON documents, and perform sophisticated aggregations beyond simple GROUP BY.

Think of SQL arrays like a shopping list: you can have multiple items in one row.
Plain-English First

Think of SQL arrays like a shopping list: you can have multiple items in one row. JSON is like a nested to-do list with sub-tasks and details. Advanced aggregation functions are like a chef who can combine ingredients in complex ways—like making a pie chart from raw data.

⚙ Browser compatibility
Latest versions — ✓ supported
ChromeFirefoxSafariEdge

In modern applications, data rarely fits neatly into simple rows and columns. You often encounter arrays of tags, JSON payloads from APIs, or need to aggregate data into complex structures for reporting. SQL databases like PostgreSQL have evolved to handle these scenarios natively with arrays, JSON support, and advanced aggregation functions.

Imagine you're building an e-commerce platform. Products have multiple categories (array), customer preferences are stored as JSON, and you need to generate a sales report that groups orders by region and product category, with subtotals and percentages. Basic GROUP BY and SUM won't cut it—you need advanced aggregation.

This tutorial will take you from basic array usage to sophisticated JSON queries and aggregation functions that go beyond simple COUNT and SUM. You'll learn how to create arrays from query results, parse JSON documents, and use advanced aggregation features like FILTER, ORDER BY within aggregates, and ordered-set aggregates. We'll also cover performance considerations and real-world debugging scenarios.

By the end, you'll be able to handle complex data structures directly in SQL, reducing the need for application-level processing and making your queries more powerful and efficient.

1. SQL Arrays: Creation and Basic Operations

SQL arrays are ordered collections of elements of the same data type. PostgreSQL supports arrays natively, allowing you to store multiple values in a single column. You can create arrays using array constructors or by aggregating values from rows.

Creating arrays from literals: ``sql SELECT ARRAY[1, 2, 3] AS numbers; SELECT ARRAY['apple', 'banana', 'cherry'] AS fruits; ``

Creating arrays from query results with array_agg(): array_agg() is an aggregate function that collects values into an array. For example, to get all product categories for each order:

``sql SELECT order_id, array_agg(product_name) AS products FROM order_items GROUP BY order_id; ``

Accessing array elements: Use subscript notation with 1-based indexing: ``sql SELECT (ARRAY['a', 'b', 'c'])[2]; -- returns 'b' ``

Array functions: - array_length(array, dimension) - returns length - unnest(array) - expands array into rows - array_remove(array, element) - removes all occurrences - array_cat(array1, array2) - concatenates arrays

Example: Using unnest to flatten arrays for aggregation: ``sql SELECT order_id, unnest(products) AS product FROM ( SELECT order_id, array_agg(product_name) AS products FROM order_items GROUP BY order_id ) sub; ``

Production insight: Be cautious with array_agg() on large datasets—it can consume significant memory. Use ORDER BY within the aggregate to control element order: array_agg(col ORDER BY col). Also, consider using array_agg(DISTINCT col) to avoid duplicates.

array_examples.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
-- Create an array from a query
SELECT department_id,
       array_agg(employee_name ORDER BY hire_date) AS employees
FROM employees
GROUP BY department_id;

-- Unnest to get individual rows
SELECT department_id, unnest(employees) AS employee
FROM (
  SELECT department_id,
         array_agg(employee_name ORDER BY hire_date) AS employees
  FROM employees
  GROUP BY department_id
) dept_emps;

-- Array functions
SELECT array_length(ARRAY[1,2,3,4], 1) AS length; -- 4
SELECT array_remove(ARRAY[1,2,3,2], 2) AS removed; -- {1,3}
SELECT array_cat(ARRAY[1,2], ARRAY[3,4]) AS concatenated; -- {1,2,3,4}
Output
department_id | employees
---------------+-------------------
1 | {Alice,Bob,Carol}
2 | {Dave,Eve}
(2 rows)
department_id | employee
---------------+----------
1 | Alice
1 | Bob
1 | Carol
2 | Dave
2 | Eve
(5 rows)
length
--------
4
(1 row)
removed
---------
{1,3}
(1 row)
concatenated
--------------
{1,2,3,4}
(1 row)
💡Array Indexing
📊 Production Insight
When using array_agg() with ORDER BY, ensure the ordering column is indexed to avoid sorting large datasets in memory.
🎯 Key Takeaway
Use array_agg() to collect values into arrays; use unnest() to expand arrays back into rows.

2. JSON and JSONB Data Types

PostgreSQL offers two JSON data types: json and jsonb. json stores an exact copy of the input text, while jsonb stores data in a decomposed binary format that allows indexing and efficient querying. For most use cases, prefer jsonb.

Creating JSONB data: ``sql SELECT '{"name": "Alice", "age": 30}'::jsonb; SELECT jsonb_build_object('name', 'Alice', 'age', 30); SELECT row_to_json(employees)::jsonb FROM employees LIMIT 1; ``

Querying JSONB: - -> returns JSON object field (as JSONB) - ->> returns JSON object field as text - #> and #>> for path access

Example: ``sql SELECT data->'name' AS name_json, data->>'name' AS name_text FROM users; ``

JSONB operators: - @> contains - ? key exists - ?| any of keys exist - ?& all keys exist

Indexing JSONB: Use GIN indexes for efficient containment and existence queries: ``sql CREATE INDEX idx_users_data ON users USING gin (data); ``

Production insight: Avoid using json type for querying; always use jsonb if you need to filter or index. When extracting paths, use #>> for text to avoid casting.

jsonb_examples.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
-- Create a table with JSONB column
CREATE TABLE products (
    id SERIAL PRIMARY KEY,
    attributes JSONB
);

-- Insert sample data
INSERT INTO products (attributes) VALUES
('{"name": "Laptop", "specs": {"ram": "16GB", "storage": "512GB"}, "tags": ["electronics", "computers"]}'),
('{"name": "Phone", "specs": {"ram": "8GB", "storage": "256GB"}, "tags": ["electronics", "mobile"]}');

-- Query nested JSON
SELECT attributes->>'name' AS name,
       attributes->'specs'->>'ram' AS ram
FROM products
WHERE attributes @> '{"tags": ["electronics"]}';

-- Check key existence
SELECT * FROM products WHERE attributes ? 'specs';
Output
name | ram
---------+-------
Laptop | 16GB
Phone | 8GB
(2 rows)
id | attributes
----+-----------
1 | ...
2 | ...
(2 rows)
🔥JSON vs JSONB
📊 Production Insight
When using @> operator, ensure the right-hand side is a valid JSONB object. For array containment, use ? operators.
🎯 Key Takeaway
Use jsonb for queryable JSON; leverage GIN indexes for performance.

3. Aggregating Rows into JSON with jsonb_agg() and jsonb_object_agg()

PostgreSQL provides aggregate functions to convert rows into JSON structures. jsonb_agg() aggregates values into a JSON array, while jsonb_object_agg() creates a JSON object from key-value pairs.

jsonb_agg(): Collects rows into a JSON array of objects. ``sql SELECT department_id, jsonb_agg(jsonb_build_object('id', employee_id, 'name', employee_name)) AS employees FROM employees GROUP BY department_id; ``

jsonb_object_agg(): Creates an object where keys come from one column and values from another. ``sql SELECT department_id, jsonb_object_agg(employee_name, salary) AS salaries FROM employees GROUP BY department_id; ``

Ordering within aggregates: Use ORDER BY inside the aggregate: ``sql jsonb_agg(employee_name ORDER BY hire_date) ``

Filtering within aggregates: Use FILTER clause: ``sql jsonb_agg(employee_name) FILTER (WHERE active = true) ``

Production insight: Be mindful of the size of the resulting JSON. For large datasets, consider paginating or using LIMIT within a subquery before aggregation.

json_aggregation.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
-- Sample data
CREATE TABLE employees (
    id SERIAL PRIMARY KEY,
    department_id INT,
    name TEXT,
    salary NUMERIC,
    active BOOLEAN
);

INSERT INTO employees (department_id, name, salary, active) VALUES
(1, 'Alice', 70000, true),
(1, 'Bob', 60000, true),
(2, 'Charlie', 80000, false),
(2, 'Diana', 75000, true);

-- jsonb_agg with ordering and filtering
SELECT department_id,
       jsonb_agg(
           jsonb_build_object('name', name, 'salary', salary)
           ORDER BY salary DESC
       ) FILTER (WHERE active) AS active_employees
FROM employees
GROUP BY department_id;

-- jsonb_object_agg
SELECT department_id,
       jsonb_object_agg(name, salary) AS salary_map
FROM employees
GROUP BY department_id;
Output
department_id | active_employees
---------------+-----------------------------------------------
1 | [{"name": "Alice", "salary": 70000}, {"name": "Bob", "salary": 60000}]
2 | [{"name": "Diana", "salary": 75000}]
(2 rows)
department_id | salary_map
---------------+-------------------------------------
1 | {"Alice": 70000, "Bob": 60000}
2 | {"Charlie": 80000, "Diana": 75000}
(2 rows)
⚠ Memory Usage
📊 Production Insight
Combine FILTER and ORDER BY within aggregates to create precise JSON structures without post-processing.
🎯 Key Takeaway
Use jsonb_agg() for arrays of objects and jsonb_object_agg() for key-value maps.

4. Advanced Aggregation Functions: Ordered-Set and Hypothetical-Set

Beyond standard aggregates, PostgreSQL offers ordered-set aggregates that operate on ordered input. Common examples include percentile_cont(), percentile_disc(), and mode().

Percentile functions: ``sql SELECT department_id, percentile_cont(0.5) WITHIN GROUP (ORDER BY salary) AS median_salary FROM employees GROUP BY department_id; ` percentile_cont returns a continuous value (interpolated), while percentile_disc` returns a discrete value from the set.

Mode: ``sql SELECT department_id, mode() WITHIN GROUP (ORDER BY salary) AS most_common_salary FROM employees GROUP BY department_id; ``

Hypothetical-set aggregates: These are used with WITHIN GROUP (ORDER BY ...) and assume a hypothetical row. Example: rank() hypothetical.

Production insight: Ordered-set aggregates require sorting, which can be expensive. Ensure the ORDER BY column is indexed. For large datasets, consider approximate algorithms if exact percentiles are not required.

ordered_set_aggregates.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
-- Percentile and mode examples
SELECT department_id,
       percentile_cont(0.5) WITHIN GROUP (ORDER BY salary) AS median,
       percentile_disc(0.5) WITHIN GROUP (ORDER BY salary) AS median_disc,
       mode() WITHIN GROUP (ORDER BY salary) AS mode_salary
FROM employees
GROUP BY department_id;

-- Multiple percentiles
SELECT department_id,
       percentile_cont(ARRAY[0.25, 0.5, 0.75]) WITHIN GROUP (ORDER BY salary) AS quartiles
FROM employees
GROUP BY department_id;
Output
department_id | median | median_disc | mode_salary
---------------+--------+-------------+-------------
1 | 65000 | 60000 | 60000
2 | 77500 | 75000 | 75000
(2 rows)
department_id | quartiles
---------------+------------------------
1 | [62500,65000,67500]
2 | [76250,77500,78750]
(2 rows)
💡Multiple Percentiles
📊 Production Insight
For large datasets, consider using percentile_disc to avoid interpolation overhead, or use approximate algorithms like t-digest via extensions.
🎯 Key Takeaway
Ordered-set aggregates like percentile_cont() compute statistics over ordered data; use WITHIN GROUP (ORDER BY ...) syntax.

5. Combining Arrays, JSON, and Aggregations: Real-World Examples

Let's combine these concepts to solve real-world problems.

Example 1: Product catalog with nested categories Each product has multiple categories (array) and attributes (JSONB). We want to generate a report of all products grouped by category, with their attributes as JSON.

``sql SELECT cat, jsonb_agg( jsonb_build_object('id', p.id, 'name', p.name, 'attrs', p.attributes) ORDER BY p.name ) AS products FROM products p CROSS JOIN LATERAL unnest(p.categories) AS cat GROUP BY cat; ``

Example 2: Employee skills matrix Employees have skills stored as an array of strings. We need to find the top 3 most common skills across departments.

``sql SELECT skill, COUNT(*) AS cnt FROM employees e CROSS JOIN LATERAL unnest(e.skills) AS skill GROUP BY skill ORDER BY cnt DESC LIMIT 3; ``

Example 3: Sales report with JSON aggregation For each region, create a JSON object mapping product categories to total sales.

``sql SELECT region, jsonb_object_agg(category, total_sales) AS sales_by_category FROM ( SELECT region, category, SUM(amount) AS total_sales FROM sales GROUP BY region, category ) sub GROUP BY region; ``

Production insight: When using CROSS JOIN LATERAL unnest(), be aware that it can multiply rows significantly. Ensure proper WHERE clauses to limit data.

real_world_examples.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
-- Example 1: Products grouped by category
SELECT cat, 
       jsonb_agg(
           jsonb_build_object('id', p.id, 'name', p.name, 'attrs', p.attributes)
           ORDER BY p.name
       ) AS products
FROM products p
CROSS JOIN LATERAL unnest(p.categories) AS cat
GROUP BY cat;

-- Example 2: Top skills
SELECT skill, COUNT(*) AS cnt
FROM employees e
CROSS JOIN LATERAL unnest(e.skills) AS skill
GROUP BY skill
ORDER BY cnt DESC
LIMIT 3;

-- Example 3: Sales by region and category
SELECT region,
       jsonb_object_agg(category, total_sales) AS sales_by_category
FROM (
    SELECT region, category, SUM(amount) AS total_sales
    FROM sales
    GROUP BY region, category
) sub
GROUP BY region;
Output
cat | products
-----+----------
... | ...
(2 rows)
skill | cnt
-------+-----
SQL | 10
Python| 8
Java | 5
(3 rows)
region | sales_by_category
--------+-----------------------------------------
East | {"Electronics": 5000, "Clothing": 3000}
West | {"Electronics": 7000, "Food": 2000}
(2 rows)
🔥LATERAL Joins
📊 Production Insight
Use LATERAL joins with unnest() carefully; they can cause performance issues if the array is large.
🎯 Key Takeaway
Combine arrays, JSON, and aggregations to build complex reports directly in SQL.

6. Performance Optimization and Indexing Strategies

When working with arrays and JSONB, indexing is crucial for performance.

Indexing JSONB: - GIN indexes are the standard for JSONB. They support @>, ?, ?|, ?& operators. - Create a GIN index on the JSONB column: CREATE INDEX ON table USING gin (jsonb_col); - For specific paths, consider a B-tree index on an expression: CREATE INDEX ON table ((jsonb_col->>'key'));

Indexing Arrays: - GIN indexes also support array operations like @> (contains), && (overlap), etc. - Example: CREATE INDEX ON table USING gin (array_col);

Query planning: - Use EXPLAIN ANALYZE to check if indexes are used. - For aggregates, ensure the GROUP BY columns are indexed.

Avoiding pitfalls: - Don't index entire JSONB if you only query a few keys; use partial indexes. - For arrays, consider using unnest() with a WHERE clause to reduce rows before aggregation.

Production insight: Monitor query performance with pg_stat_statements and adjust indexes based on actual query patterns.

indexing_strategies.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
-- GIN index on JSONB column
CREATE INDEX idx_products_attributes ON products USING gin (attributes);

-- B-tree index on a specific JSONB key
CREATE INDEX idx_products_name ON products ((attributes->>'name'));

-- GIN index on array column
CREATE INDEX idx_employees_skills ON employees USING gin (skills);

-- Partial index for active employees only
CREATE INDEX idx_active_employees ON employees (department_id) WHERE active = true;

-- Check query plan
EXPLAIN ANALYZE
SELECT * FROM products
WHERE attributes @> '{"tags": ["electronics"]}';
Output
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on products (cost=... rows=... width=...) (actual time=... rows=... loops=1)
Recheck Cond: (attributes @> '{"tags": ["electronics"]}'::jsonb)
-> Bitmap Index Scan on idx_products_attributes (cost=... rows=... width=...) (actual time=... rows=... loops=1)
Index Cond: (attributes @> '{"tags": ["electronics"]}'::jsonb)
Planning Time: 0.123 ms
Execution Time: 0.456 ms
⚠ Index Maintenance
📊 Production Insight
Partial indexes can significantly improve performance for queries that filter on a subset of rows.
🎯 Key Takeaway
Use GIN indexes for JSONB and array columns; use expression indexes for specific keys.

7. Common Pitfalls and Best Practices

Pitfall 1: Mixing JSON and JSONB Using json type for querying leads to poor performance. Always use jsonb for queryable data.

Pitfall 2: Unbounded aggregation Aggregating thousands of rows into a single JSON object can cause memory issues. Always limit the input or paginate the output.

Pitfall 3: Ignoring NULLs in array_agg array_agg includes NULLs by default. Use FILTER (WHERE col IS NOT NULL) or array_remove() to clean up.

Best Practices: - Use jsonb_build_object and jsonb_build_array for constructing JSON programmatically. - Prefer jsonb_agg over array_agg when you need nested objects. - Use ORDER BY within aggregates to control element order. - For large datasets, consider materialized views to pre-aggregate data. - Test with realistic data volumes in staging before deploying to production.

best_practices.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
-- Good: Use jsonb_build_object for clarity
SELECT jsonb_agg(
    jsonb_build_object('id', id, 'name', name)
    ORDER BY name
) FROM employees;

-- Bad: Concatenating strings to build JSON
SELECT '[' || string_agg('{"id":' || id || ',"name":"' || name || '"}', ',') || ']'
FROM employees;

-- Good: Filter out NULLs
SELECT array_agg(name) FILTER (WHERE name IS NOT NULL) FROM employees;

-- Bad: Relying on default
SELECT array_agg(name) FROM employees;

-- Good: Limit aggregation input
SELECT jsonb_agg(sub.*) FROM (
    SELECT * FROM employees ORDER BY id LIMIT 100
) sub;
Output
-- No output, just examples
💡Use jsonb_build_object
📊 Production Insight
Always validate JSON structure in application code before inserting into the database to avoid malformed data.
🎯 Key Takeaway
Avoid common pitfalls by using proper data types, filtering NULLs, and limiting aggregation sizes.
● Production incidentPOST-MORTEMseverity: high

The JSON Aggregation That Broke the Dashboard

Symptom
The analytics dashboard showed 'Out of memory' errors and failed to load for large customers.
Assumption
The developer assumed that aggregating JSON into a single row would be efficient because it reduced row count.
Root cause
Using jsonb_agg() on a large dataset without limiting the number of rows caused a single JSON object to become enormous, exceeding PostgreSQL's work_mem and causing disk spills.
Fix
Added pagination to the aggregation query using LIMIT and OFFSET, and increased work_mem temporarily. Also added a GIN index on the JSONB column to speed up filtering.
Key lesson
  • Always test aggregation queries with production-scale data volumes.
  • Use LIMIT or WHERE clauses to restrict the size of aggregated JSON objects.
  • Monitor work_mem and consider increasing it for large aggregations, but prefer query optimization.
  • Consider using paginated results or streaming APIs for very large datasets.
  • Index JSONB columns used in WHERE clauses to avoid sequential scans.
Production debug guideSymptom to Action4 entries
Symptom · 01
Query returns unexpected NULLs in array_agg() results
Fix
Check if the source column contains NULLs; use array_agg(column) FILTER (WHERE column IS NOT NULL) to exclude them.
Symptom · 02
jsonb_extract_path_text() returns NULL for valid JSON
Fix
Verify the JSON path is correct; use jsonb_typeof() to check the value type. Paths are case-sensitive.
Symptom · 03
Aggregation query runs slowly on large tables
Fix
Check if appropriate indexes exist; for JSONB, use GIN indexes. For array operations, consider using unnest() with caution.
Symptom · 04
Memory errors when using jsonb_agg()
Fix
Reduce the result set size with WHERE clauses or LIMIT. Increase work_mem temporarily if necessary.
★ Quick Debug Cheat SheetCommon issues and immediate actions for SQL arrays, JSON, and advanced aggregation.
array_agg() includes NULLs
Immediate action
Add FILTER (WHERE column IS NOT NULL)
Commands
SELECT array_agg(col) FILTER (WHERE col IS NOT NULL) FROM table;
SELECT array_remove(array_agg(col), NULL) FROM table;
Fix now
Use array_remove() to strip NULLs after aggregation.
JSON path extraction fails+
Immediate action
Check path with jsonb_extract_path() and verify JSON structure
Commands
SELECT jsonb_extract_path(data, 'key1', 'key2') FROM table;
SELECT jsonb_typeof(data -> 'key1') FROM table;
Fix now
Use the -> operator to navigate and check types.
Aggregation too slow+
Immediate action
Add index on columns used in GROUP BY and WHERE
Commands
CREATE INDEX ON table (group_col);
EXPLAIN ANALYZE SELECT ...;
Fix now
Add a composite index on (group_col, order_col) if ordering within aggregate.
FunctionPurposeExample
array_agg()Collect values into an arrayarray_agg(col ORDER BY col)
jsonb_agg()Aggregate rows into JSON arrayjsonb_agg(row_to_json(t))
jsonb_object_agg()Aggregate key-value pairs into JSON objectjsonb_object_agg(key, value)
percentile_cont()Continuous percentilepercentile_cont(0.5) WITHIN GROUP (ORDER BY col)
mode()Most frequent valuemode() WITHIN GROUP (ORDER BY col)
⚙ Quick Reference
7 commands from this guide
FileCommand / CodePurpose
array_examples.sqlSELECT department_id,1. SQL Arrays
jsonb_examples.sqlCREATE TABLE products (2. JSON and JSONB Data Types
json_aggregation.sqlCREATE TABLE employees (3. Aggregating Rows into JSON with jsonb_agg() and jsonb_obj
ordered_set_aggregates.sqlSELECT department_id,4. Advanced Aggregation Functions
real_world_examples.sqlSELECT cat,5. Combining Arrays, JSON, and Aggregations
indexing_strategies.sqlCREATE INDEX idx_products_attributes ON products USING gin (attributes);6. Performance Optimization and Indexing Strategies
best_practices.sqlSELECT jsonb_agg(7. Common Pitfalls and Best Practices

Key takeaways

1
Use jsonb for JSON data and GIN indexes for performance.
2
Leverage array_agg(), jsonb_agg(), and jsonb_object_agg() to aggregate data into complex structures.
3
Ordered-set aggregates like percentile_cont() provide statistical calculations directly in SQL.
4
Always handle NULLs explicitly in array aggregations.
5
Test aggregation queries with production-scale data to avoid memory issues.

Common mistakes to avoid

4 patterns
×

Using json instead of jsonb for queryable data

×

Forgetting to handle NULLs in array_agg

×

Aggregating large datasets into a single JSON object without limits

×

Using string concatenation to build JSON

INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01SENIOR
Explain the difference between jsonb_agg() and jsonb_object_agg(). Give ...
Q02SENIOR
How would you find the median salary per department using SQL?
Q03SENIOR
What are ordered-set aggregates? Provide an example.
Q04JUNIOR
How do you index a JSONB column for containment queries?
Q01 of 04SENIOR

Explain the difference between jsonb_agg() and jsonb_object_agg(). Give an example of when to use each.

ANSWER
jsonb_agg() aggregates values into a JSON array, while jsonb_object_agg() creates a JSON object from key-value pairs. Use jsonb_agg() when you need a list of items (e.g., all employees in a department), and jsonb_object_agg() when you need a mapping (e.g., employee name to salary).
FAQ · 5 QUESTIONS

Frequently Asked Questions

01
What is the difference between json and jsonb?
02
How do I remove NULLs from an array_agg result?
03
Can I index a specific key inside a JSONB column?
04
What is the maximum size of a JSONB value?
05
How do I aggregate rows into a JSON object with dynamic keys?
N
Naren Founder & Principal Engineer

20+ years shipping high-throughput database systems. Lessons pulled from things that broke in production.

Follow
Verified
production tested
July 13, 2026
last updated
2,165
articles · all by Naren
🔥

That's SQL Advanced. Mark it forged?

5 min read · try the examples if you haven't

Previous
Multi-version Concurrency Control
17 / 17 · SQL Advanced
Next
Introduction to NoSQL Databases