Tier 4 · Advanced

JSONB Field Extraction in SQL

By Owen Middleton · Updated September 2026 · Examples run on PostgreSQL 17

How do you extract a field from JSONB in SQL?

The -> and ->> operators pull values out of JSONB columns — one field at a time, one row at a time. They're what you use when a column stores semi-structured data and you need to work with the values inside it like regular SQL columns.

The difference between the two comes down to what they return. -> returns the extracted value as JSONB, preserving its structure so you can chain more extractions onto it. ->> returns the extracted value as plain text — ready for display or comparison, but all type information is gone.

SELECT name,
  attributes->>'color' AS color,
  (attributes->>'weight_kg')::numeric AS weight_kg
FROM products
WHERE attributes IS NOT NULL
LIMIT 10

The ->>'color' operator extracts color and returns it as plain text. The (attributes->>'weight_kg')::numeric extracts weight_kg as text and casts it to a number — the cast is what lets you compare it against numeric thresholds. Each ->> gives you the terminal value as text; when the value is a number or date, you always need an explicit cast to work with it.

How do you read a nested JSONB field in PostgreSQL?

When the JSONB has nested objects (like {'address': {'city': 'London'}}), use -> to navigate into the nested level as JSONB, then ->> for the final field:

metadata -> 'address' ->> 'city'  -- navigates into address, returns city as text

Array elements use the same operators with integer indexes: -> 0 returns the first element as JSONB, ->> 0 returns it as text.

JSONB columns show up often in practice: an events table with a properties column storing arbitrary attributes, a products table with a metadata field for flexible specs, or a log table storing raw API payloads. Each row can have a different set of keys. You can't reference these fields with a regular column name — the extraction operators are how you get to them.

What is the difference between -> and ->> in PostgreSQL?

The one thing that trips people up

->> always returns text. If the JSONB field contains a number, ->> returns the string '80', not the number 80. Comparing or doing arithmetic with it requires an explicit cast:

WHERE (metadata ->> 'status') = 'active'
AND   (metadata ->> 'score')::numeric > 80

The status comparison works without a cast because the target is already text. The score comparison needs ::numeric because you're comparing against a number. Skip the cast and PostgreSQL will raise a type error.

Navigating into a missing key returns NULL rather than an error, so a field that doesn't exist in one row's JSON silently becomes NULL in the output — which is usually correct for heterogeneous data, but worth knowing if you're assuming the key is always present.

For queries that filter on JSONB fields frequently, be aware that extraction in WHERE runs a full table scan unless there's a GIN index on the column. The query is correct either way, but performance can suffer on large tables without an index.

What do the #> and #>> JSONB operators do?

Deep paths with `#>` and `#>>`

For deeply nested structures, PostgreSQL also supports path-based operators. #> takes an array of keys as the path: metadata #> '{address, city}' is equivalent to metadata -> 'address' -> 'city'. #>> returns the same path as text, like ->>. These are useful when the path is longer or when you want to express nested navigation in a single operator. For one or two levels of nesting, chaining -> and ->> is clearer. For three or more levels, the path operators are easier to read.

Practice JSONB Field Extraction in SQL

Practice · easy ecommerce · Brightlane

Brightlane's product merchandising system displays color attributes for catalog items.

Write a query to return the ID, name, and color attribute for product id = 7.

Assumptions:

  • The products table has one row per product with an id, a name, and an attributes JSONB column.
  • Each product's attributes value is a JSONB object whose keys depend on the product type.
  • Product id = 7 has a 'color' key in its attributes. The result should pull that value out as plain text.

Output:

  • A single row with columns id, name, and color.
Schema · ecommerce5 tables? = nullable
categories
idinteger
nametext
parent_id?integer
products
idinteger
nametext
category_id?integer
pricenumeric
stock_qtyinteger
attributes?jsonb
order_items
idinteger
order_id?integer
product_id?integer
quantityinteger
unit_pricenumeric
customers
idinteger
nametext
emailtext
city?text
countrytext
created_attimestamptz
is_activeboolean
orders
idinteger
customer_id?integer
ordered_attimestamptz
statustext
total_amountnumeric

Run previews · Check grades

Write a query, then run it to see results here.

Worked solution

The full breakdown walks through the shape, each clause, why this approach beats the alternatives, and the trap to avoid.

See the full worked solution

10 JSONB Field Extraction practice problems

Start learning to practice all 10 JSONB Field Extraction problems, with instant grading and mastery tracking.

Deeper guides on JSONB Field Extraction

Common questions about JSONB Field Extraction

What happens when a JSONB key does not exist?

You get NULL rather than an error, with both extraction operators. That is usually right for data where rows carry different keys, but it does mean a misspelled key looks exactly like a key that is genuinely absent.

Why does comparing an extracted number fail?

Because the text operator always returns text, so comparing it with a number raises an operator does not exist error. Cast the extracted value to numeric first and the comparison behaves as you expect.

Which extraction operator should you use?

The one returning JSONB while you are still navigating, and the one returning text when you have arrived. Chaining the first gets you through nested objects; the second gives you a value you can cast, compare or display.

How you actually get good at SQL

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.

A stack of SQL practice problem cards, the top card showing an employees table.
615 problems · 66 concepts

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.

A retro computer showing a SQL query marked correct with a green checkmark.
Instant AI feedback

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.

A circular mastery progress dial filling from blue to green, the SQLMaxx diamond at its center.
Mastery tracking

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.

A SQL query editor circled by a blue return arrow with a clock, scheduled to come back for review.
Spaced review

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.