How JSON columns and JSON literals in MySQL turn into virtual tables
JSON_TABLE is one of the lesser known but genuinely useful functions in MySQL 8: it turns a JSON document, whether stored as a column value or written as a literal directly in the query, into a regular result set that can be joined with other tables. Instead of awkwardly pulling JSON values out through several JSON_EXTRACT calls, JSON_TABLE directly produces rows and columns that behave like any other table. This article covers the basic mechanics, concrete use cases around Magento attribute data, and where the performance limits sit compared to a genuinely normalized table.
Table of Contents
- 1. What JSON_TABLE fundamentally does
- 2. Basic syntax with a simple example
- 3. Practical case: querying Magento attribute JSON data relationally
- 4. Resolving nested JSON structures with NESTED PATH
- 5. Handling missing or mistyped values
- 6. JSON_TABLE compared to JSON_EXTRACT and its alternatives
- 7. Performance limits compared to normalized tables
- 8. When JSON_TABLE pays off and when a real table is better
- 9. Practical example: JSON import validation before insert
- 10. Summary
- 11. FAQ
1. What JSON_TABLE fundamentally does
JSON_TABLE takes a JSON document as its first parameter, either a column of type JSON, a subsection extracted from another column, or a JSON literal written directly in the query, and evaluates it against a described column schema. The result is a perfectly ordinary, table like result set with named columns and matching SQL data types, which can then be used in FROM or JOIN clauses just like any other derived table.
The key difference from JSON_EXTRACT is that JSON_TABLE does not just return a single value or a single sub document, it automatically produces multiple rows for arrays, one per array element. Nested structures with several levels can also be resolved directly through nested NESTED PATH clauses, without having to split the query into several separately joined subqueries.
2. Basic syntax with a simple example
The simplest form of JSON_TABLE works directly with a JSON literal and demonstrates the basic syntax without any table being involved at all. Each column in the COLUMNS block gets a name, a target type, and a JSON path that locates the corresponding value within the document.
For the base level of an array, the path '$[*]' is enough, combined with FOR ORDINALITY for an automatically generated row number and relative paths like '$.name' for the individual fields of each array element. This basic structure forms the foundation for every more complex use case involving actual column values.
SELECT *
FROM JSON_TABLE(
'[{"sku":"WS-01","qty":12},{"sku":"WS-02","qty":5}]',
'$[*]'
COLUMNS (
row_num FOR ORDINALITY,
sku VARCHAR(64) PATH '$.sku',
qty INT PATH '$.qty'
)
) AS jt;
3. Practical case: querying Magento attribute JSON data relationally
In custom extended Magento data models, structured extra information often ends up in its own JSON column, for example a list of configurable variant metadata or a collection of supplier prices per quantity break, instead of creating a dedicated EAV attribute for every single case. These columns are convenient for read access from application code, but they resist classic SQL analysis as long as they are treated as a plain JSON blob.
With JSON_TABLE, such a column can be expanded directly at row level within a SELECT and joined with other tables, for example to compare each tier price row of a product against its catalog price or stock level. Reports that previously would have needed their own application logic for JSON processing can instead be written as plain SQL and plugged directly into existing reporting tools.
SELECT p.sku, jt.qty_from, jt.price
FROM catalog_product_extra p
JOIN JSON_TABLE(
p.tier_price_json,
'$[*]'
COLUMNS (
qty_from INT PATH '$.qty_from',
price DECIMAL(10,2) PATH '$.price'
)
) AS jt
WHERE p.sku = 'WS-01';
4. Resolving nested JSON structures with NESTED PATH
As soon as a JSON document contains several levels of nesting, for example a product with a list of variants, each of which in turn contains a list of warehouse locations with stock quantities, a single PATH expression is no longer enough. This is where the NESTED PATH clause inside the COLUMNS block comes in, allowing another level of expansion directly within the same JSON_TABLE definition.
The result is a fully flattened result set where outer fields such as the product SKU are automatically duplicated onto every inner row, exactly like a classic SQL join between two normalized tables. Without NESTED PATH, the same evaluation would have to be replicated through several nested JSON_TABLE calls with a manual join, which becomes markedly harder to follow.
SELECT jt.sku, jt.warehouse, jt.stock
FROM JSON_TABLE(
variant_stock_json,
'$.variants[*]'
COLUMNS (
sku VARCHAR(64) PATH '$.sku',
NESTED PATH '$.locations[*]'
COLUMNS (
warehouse VARCHAR(32) PATH '$.warehouse',
stock INT PATH '$.stock'
)
)
) AS jt;
5. Handling missing or mistyped values
JSON documents from external sources are rarely as cleanly structured as a strictly validated table of your own, which is why JSON_TABLE allows explicit ERROR and EMPTY handling for every column. If a path is completely missing from the document, NULL ON EMPTY applies by default, while a type conflict, for example text where an INT was declared, causes a hard error for the entire query if left unspecified.
For production queries it is therefore worth explicitly specifying DEFAULT values or NULL behavior for both cases instead of relying on the implicit defaults. Especially with JSON data imported from or delivered by third party systems, this explicit handling prevents a single malformed row from crashing the entire report.
COLUMNS (
sku VARCHAR(64) PATH '$.sku' DEFAULT 'unknown' ON EMPTY,
price DECIMAL(10,2) PATH '$.price'
DEFAULT '0.00' ON EMPTY DEFAULT '0.00' ON ERROR
)
6. JSON_TABLE compared to JSON_EXTRACT and its alternatives
JSON_EXTRACT remains the right choice when only a single value or a single sub document needs to be read from a JSON column, for example as an extra column in an otherwise regular query. But as soon as a JSON array needs to be expanded into several SQL rows, JSON_EXTRACT based code quickly becomes hard to follow, because it typically requires a recursive CTE over a numeric index or an external helper table with sequential numbers.
JSON_TABLE handles this row generation natively and readably within a single clause, making the code both shorter and easier for other developers to follow. For complex, multi level nesting, JSON_TABLE holds a clear advantage over any combination of JSON_EXTRACT and a manual row generating trick.
7. Performance limits compared to normalized tables
As useful as JSON_TABLE is for analysis, it remains a runtime resolution with no index support whatsoever within the JSON document itself. Every query has to parse the entire JSON document and re evaluate every referenced path, there is no way to read only part of a large JSON array selectively, the way an index on a normalized row table would allow.
For small to medium sized documents and occasional analysis, for example in a daily report, this overhead is negligible in practice. For very large arrays with thousands of elements per row, or for queries that run at second level frequency within the application itself, the repeated parsing cost quickly becomes a bottleneck, and a genuinely normalized child table with a matching foreign key index remains the clearly faster choice.
8. When JSON_TABLE pays off and when a real table is better
JSON_TABLE is particularly well suited for ad hoc reporting, data migrations, and import validation, where JSON data needs to be turned into relational form once or occasionally, without setting up a dedicated new table schema for it. It is also a pragmatic intermediate solution for flexible, frequently changing extra attributes whose structure is not yet fixed.
Once a data model has stabilized and is queried regularly, especially under heavy load in ongoing application operation, rebuilding it into a genuine normalized child table with its own indexes is almost always worth it. JSON_TABLE then remains a useful tool for a one time migration of the existing JSON data into that new schema, but not as the permanent access layer for production, frequently executed queries.
9. Practical example: JSON import validation before insert
When connecting external supplier feeds that deliver product data as a nested JSON document, JSON_TABLE works excellently as a validation layer between raw import and the actual insert into the target tables. The raw data first lands unchanged in a staging column, then a JSON_TABLE query systematically checks for missing required fields, implausible values, or duplicate SKUs before any INSERT into the actual product tables happens at all.
This approach consistently moves validation logic into the database, instead of implementing it exclusively in application code, and thereby makes it reusable for every further import process, regardless of which application actually triggers the import.
| Approach | Rows from array | Nesting | Index support | Typical use |
|---|---|---|---|---|
| JSON_EXTRACT | manual, cumbersome | single level only, nesting is hard | none within the document | single value, filter condition |
| JSON_TABLE | native, automatic | directly via NESTED PATH | none within the document | ad hoc reporting, migration |
| Generated column plus index | not applicable | flat fields only | fully indexable | frequently filtered single value |
| Normalized child table | regular JOIN | arbitrary via further tables | fully indexable | production, high load access |
Mironsoft
Database performance, index tuning, and Magento DB optimization
A Magento shop suffering from slow database queries?
We analyze MySQL databases for performance bottlenecks, optimize indexes and queries with purpose, and set up backup and replication strategies that actually work when it counts.
Performance Audit
Systematically investigate the slow query log and explain plans for bottlenecks.
Index Optimization
Build indexes with purpose for the shop's actual query load.
Backup Strategy
Set up reliable backup and restore processes for production Magento databases.
10. Summary
JSON_TABLE: Key Takeaways
Virtual table from JSON
JSON_TABLE turns JSON columns or JSON literals into a regular, joinable result set.
Arrays become rows
Every array element automatically produces its own row, nested arrays via NESTED PATH.
Not an index substitute
JSON_TABLE reads and parses the entire document at runtime, with no index support inside the JSON.
Ideal for migration and reporting
For stable, high load access patterns, a genuine normalized child table remains the faster choice.