Interview Q&A / PostgreSQL

BeginnerIn-depth3 min readUpdated 2 Oct 2026

What is the difference between JSON and JSONB in PostgreSQL?

SHORT ANSWER

JSON stores the exact text you inserted, including spaces, key order and duplicate keys, and re-parses it on every read. JSONB stores a parsed binary form, which is slightly slower to write but much faster to query and can be indexed with GIN. Use JSONB unless you must keep the original text exactly.

Explain it simply

JSON is like saving a photocopy of a handwritten form. You keep it exactly as written, but every time someone asks "what is the phone number?", you have to read the whole page again. JSONB is like typing the form into a spreadsheet once. You lose the handwriting and the order the fields were written in, but finding any field afterwards is quick, and you can build an index on it.

Example

sql
CREATE TABLE products (
  id         bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  name       text NOT NULL,
  attributes jsonb NOT NULL DEFAULT '{}'
);

INSERT INTO products (name, attributes) VALUES
  ('Kurta', '{"size": "M", "colour": "blue", "tags": ["cotton", "festive"]}'),
  ('Phone', '{"brand": "Lava", "ram_gb": 6}');

-- -> returns jsonb, ->> returns text
SELECT name, attributes->>'colour' AS colour FROM products;

-- Containment: does the document contain this key/value?
SELECT name FROM products WHERE attributes @> '{"brand": "Lava"}';

-- Key existence
SELECT name FROM products WHERE attributes ? 'size';

-- Index to make @> and ? fast
CREATE INDEX idx_products_attributes ON products USING gin (attributes);

The difference in storage shows up here:

sql
SELECT '{"b": 1, "a": 2, "a": 3}'::json;   -- {"b": 1, "a": 2, "a": 3}
SELECT '{"b": 1, "a": 2, "a": 3}'::jsonb;  -- {"a": 3, "b": 1}

How it works

JSONJSONB
Stored asExact input textParsed binary format
Whitespace and key orderKeptNot kept
Duplicate keysAll keptLast value wins
Insert speedSlightly fasterSlightly slower (parsing)
Query speedSlower, parsed every timeFaster
GIN indexing and operators like @>, ?NoYes

Both types validate that the input is valid JSON. Most JSON functions have a jsonb_ version, such as jsonb_set to update one key and jsonb_array_elements to turn an array into rows.

Real-world use

  • Product attributes that differ by category: a phone has RAM, a kurta has a size.
  • Storing raw webhook payloads from a payment gateway, alongside a few extracted columns you query often.
  • User settings or feature flags that change shape over time.

Use plain JSON only when you need the exact original text back, for example to verify a signature that was computed on the raw payload.

Common mistakes

  • Putting everything in one JSONB column. Fields you filter, join or sort on regularly, such as user_id or created_at, belong in normal typed columns with constraints and foreign keys.
  • Comparing attributes->'ram_gb' = 6. The -> operator returns jsonb. Use (attributes->>'ram_gb')::int = 6, or containment attributes @> '{"ram_gb": 6}'.
  • Expecting a GIN index to speed up attributes->>'colour' = 'blue'. The default GIN index supports @>, ?, ?| and ?&. For one specific key, create a B-tree expression index on (attributes->>'colour').

What to say in the interview

"JSON stores the input text as is, keeping whitespace, key order and duplicates, and it is parsed every time you query it. JSONB stores a decomposed binary form, drops whitespace and duplicate keys, and is a bit slower to insert but much faster to query. JSONB supports GIN indexes and operators like containment and key existence. I default to JSONB, keep frequently queried fields as real columns, and use JSON only if I need the original text exactly."

Follow-up questions

  • What is the difference between -> and ->>?
  • How do you update a single key inside a JSONB column?
  • When would you choose a separate table instead of a JSONB column?

Go deeper

Read these once the basics above feel easy.

GIN has a second operator class, jsonb_path_ops: CREATE INDEX ON products USING gin (attributes jsonb_path_ops);. It is smaller and faster for @> queries but does not support the key-existence operators. PostgreSQL also supports SQL/JSON path queries such as jsonb_path_query(attributes, '$.tags[*]') and the @? and @@ operators, which the default GIN operator class can also accelerate.

#postgresql#json#jsonb#data-types

Related questions

Practise more PostgreSQL questions

Test your recall with short answers, then return to the detailed explanations when you need context.

Open PostgreSQL quick answers →

Get new questions like this by email

One short email when something new and useful lands. No spam, unsubscribe in one tap.

By subscribing you agree to the Privacy Policy.