CONCEPT
Postgres JSONB vs MySQL JSON¶
Postgres has two JSON types: JSON (text storage) and
JSONB (binary storage, indexable, more compact).
MySQL has one: JSON. At the capability layer the two
engines are close; at the engine layer Postgres's JSONB
is typically more storage-efficient.
What each engine gives you¶
- Postgres
JSONB— binary in-engine representation. Supports: - GIN indexes over the whole document.
- Key-path operators (
->,->>,#>,#>>). - Containment and existence (
@>,?,?&,?|). - Inline modification (
jsonb_set). - MySQL
JSON— binary in-engine representation. Supports: - Functional indexes on extracted keys (see concepts/functional-index-mysql).
- Key-path extraction (
->,->>). - Inline modification (
JSON_SET,JSON_INSERT,JSON_REPLACE,JSON_REMOVE). - Multi-valued indexes on JSON arrays (MySQL 8.0.17+).
Migration implication¶
In a Postgres → MySQL audit:
the
productscolumn, on the other hand, is of typejsonb, or binary JSON. While MySQL at the moment does not have support for thejsonbdata type, it does have support forjson, so if we set the data type for theproductscolumn asjson, we'll get a very similar experience.—
The drop-in answer is Postgres JSONB → MySQL JSON. The
feature gap at the query layer is small: a JSON-heavy
workload will generally port. For how far MySQL JSON goes
at the indexing layer see
.
Where the gap still bites¶
- Storage efficiency — Postgres's
JSONBis typically more compact on disk because it shares key-name storage across rows. MySQL'sJSONencoding is similar in spirit but not bit-identical, and measured sizes diverge. - GIN index over the whole document — Postgres supports a single GIN index that answers arbitrary containment queries; MySQL requires a per-path functional index.
- Operator ecosystem — Postgres's
@>,?,?&,?|have no one-to-one MySQL equivalents; they rewrite toJSON_CONTAINS,JSON_CONTAINS_PATH,JSON_OVERLAPS.
PlanetScale-specific caveats¶
- Per-document ceiling — PlanetScale caps MySQL
JSONdocuments at 67 MB (vs MySQL's 1 GB theoretical). PostgresJSONBunder PlanetScale for Postgres has its own policy, not equated here. JSON_TABLEgap — PlanetScale supports every MySQLJSONfunction exceptJSON_TABLE(the one that pivots a JSON array into relational rows). A Postgresjsonb_to_recordsetport needs a client- side loop or a stored-procedure unnesting.
(Source: .)
Related¶
- concepts/cross-engine-sql-data-type-equivalence
- concepts/mysql-json-data-type — canonical wiki page for the MySQL side of this comparison.
- concepts/json-data-type-storage-overhead — the 4–10 bytes/string rule-of-thumb that extends the "storage efficiency" bullet above with a quantitative rule.
- concepts/mysql-json-extraction-operator — the
->/->>operators that provide the MySQL side of the key-path-extraction equivalence. - concepts/functional-index-mysql — the MySQL indexing mechanism that approximates Postgres's GIN over a single JSON path.
- patterns/cross-engine-database-migration-audit