Skip to main content

MySQL JSON

Learning Focus

MySQL's JSON column type validates JSON at insert time and provides rich functions for extraction and modification using JSONPath expressions.

Create and Insert

CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
profile JSON
);

INSERT INTO users (name, profile) VALUES
('Alice', '{"age": 30, "city": "Singapore", "tags": ["dev", "admin"]}'),
('Bob', '{"age": 25, "city": "Jakarta", "tags": ["editor"]}');

Extract Values

-- JSON_EXTRACT: returns JSON-typed value (quoted strings)
SELECT JSON_EXTRACT(profile, '$.city') FROM users; -- "Singapore"

-- ->> shorthand: returns unquoted text
SELECT profile->>'$.city' FROM users; -- Singapore

-- Nested path
SELECT profile->>'$.address.postal' FROM users;

Key Functions

FunctionPurpose
JSON_EXTRACT(col, path)Extract value
JSON_UNQUOTE(val)Remove surrounding quotes
JSON_SET(col, path, val)Insert or update
JSON_INSERT(col, path, val)Insert only if missing
JSON_REPLACE(col, path, val)Replace only if exists
JSON_REMOVE(col, path)Delete value
JSON_CONTAINS(col, val)Containment check
JSON_LENGTH(col)Array or object length
JSON_KEYS(col)All top-level keys

Filtering

-- Filter by JSON value
SELECT name FROM users WHERE profile->>'$.city' = 'Singapore';

-- Array contains value
SELECT name FROM users
WHERE JSON_CONTAINS(profile->'$.tags', '"admin"');

-- Count tag array length
SELECT name, JSON_LENGTH(profile->'$.tags') AS tag_count FROM users;

Updating

UPDATE users SET profile = JSON_SET(profile, '$.city', 'Kuala Lumpur')
WHERE name = 'Bob';

UPDATE users SET profile = JSON_SET(profile, '$.verified', TRUE)
WHERE name = 'Alice';

UPDATE users SET profile = JSON_REMOVE(profile, '$.tags')
WHERE name = 'Bob';

Generated Columns for Indexing

MySQL cannot index a raw JSON column — use a generated column:

ALTER TABLE users
ADD COLUMN city VARCHAR(100) GENERATED ALWAYS AS (profile->>'$.city') STORED,
ADD INDEX idx_city (city);

SELECT name FROM users WHERE city = 'Singapore'; -- uses index

Concept Map

Concept Flow

MySQL JSON Column
├── JSON_EXTRACT / ->> → Query and Filter
├── JSON_SET / JSON_INSERT → Modify Values
└── Generated Column + Index → B-tree Index on JSON path

Common Pitfalls

PitfallConsequencePrevention
Direct index on JSON columnError — not supportedUse generated column + index
JSON_EXTRACT for text comparisonQuoted string mismatchUse ->> for unquoted text
JSON_INSERT when key existsOld value keptUse JSON_SET for upsert

What's Next