MySQL
arrays
database storage
SQL tips
data management

How to store arrays in MySQL?

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

MySQL is relational, so storing arrays directly in one column is usually not the first design choice. The best approach depends on how you query, validate, and update the values. This guide compares normalized tables, JSON columns, and string-based fallbacks, including when each option is appropriate.

Preferred Relational Model: Child Table

If each parent record has many values, model them in a separate table. This keeps queries efficient and enforces integrity with foreign keys.

sql
1CREATE TABLE users (
2  id BIGINT PRIMARY KEY AUTO_INCREMENT,
3  name VARCHAR(100) NOT NULL
4);
5
6CREATE TABLE user_phone_numbers (
7  id BIGINT PRIMARY KEY AUTO_INCREMENT,
8  user_id BIGINT NOT NULL,
9  phone VARCHAR(30) NOT NULL,
10  FOREIGN KEY (user_id) REFERENCES users(id),
11  INDEX idx_user_phone_user_id (user_id)
12);

Insert and query:

sql
1INSERT INTO users(name) VALUES ('Alice');
2INSERT INTO user_phone_numbers(user_id, phone)
3VALUES (1, '111-1111'), (1, '222-2222');
4
5SELECT u.name, p.phone
6FROM users u
7JOIN user_phone_numbers p ON p.user_id = u.id
8WHERE u.id = 1;

This pattern is scalable and query-friendly.

JSON Column for Flexible Schemas

If values are semi-structured and query patterns are light, JSON can be acceptable.

sql
1CREATE TABLE products (
2  id BIGINT PRIMARY KEY AUTO_INCREMENT,
3  sku VARCHAR(50) NOT NULL,
4  tags JSON NOT NULL
5);
6
7INSERT INTO products(sku, tags)
8VALUES ('SKU-1', JSON_ARRAY('new', 'sale', 'featured'));

Query for membership:

sql
SELECT *
FROM products
WHERE JSON_CONTAINS(tags, JSON_QUOTE('sale'));

JSON is convenient, but indexing and joins are less straightforward than normalized tables.

Update Array-Like Data in JSON

MySQL provides functions to append or modify JSON arrays.

sql
UPDATE products
SET tags = JSON_ARRAY_APPEND(tags, '$', 'clearance')
WHERE id = 1;

For predictable behavior, define application-level rules for duplicates and tag ordering.

CSV String Storage Is Usually a Last Resort

Storing comma-separated values in a text column is easy to implement but difficult to query and validate.

sql
1CREATE TABLE bad_example (
2  id BIGINT PRIMARY KEY AUTO_INCREMENT,
3  values_csv TEXT NOT NULL
4);

This approach breaks normalization and complicates indexing, filtering, and updates. Prefer it only for temporary migrations or legacy compatibility.

Choosing the Right Strategy

Use this decision guide:

  • Need relational joins, indexing, and constraints: use child table.
  • Need flexible shape and moderate query complexity: use JSON.
  • Legacy import or temporary storage only: CSV as transitional format.

Schema choice should follow query patterns, not short-term coding convenience.

Performance and Integrity Considerations

  • Child tables provide the best long-term query performance.
  • JSON can perform well with proper generated columns and indexes.
  • CSV often causes full scans and brittle parsing logic.

If array elements need uniqueness, enforce it in schema for child tables or in application validation for JSON.

Indexing JSON Array Content

If JSON arrays are queried frequently, create generated columns for common predicates and index those columns.

sql
1ALTER TABLE products
2ADD COLUMN has_sale_tag TINYINT AS (
3  JSON_CONTAINS(tags, JSON_QUOTE('sale'))
4) STORED,
5ADD INDEX idx_products_has_sale_tag (has_sale_tag);

Then queries become faster and easier to optimize:

sql
SELECT id, sku
FROM products
WHERE has_sale_tag = 1;

Generated columns can bridge flexibility and performance when full normalization is not feasible.

Migration Path from CSV to Relational Model

If legacy data is already stored as comma-separated strings, migrate incrementally.

  • Create new child table.
  • Backfill values in batches.
  • Switch writes to the new schema.
  • Remove legacy column after validation.

A staged migration reduces risk and avoids downtime for large tables.

Common Pitfalls

  • Packing relational data into one column and losing query flexibility.
  • Choosing JSON without understanding indexing implications.
  • Using CSV values and relying on string matching for business logic.
  • Ignoring data validation for array element format and duplicates.
  • Migrating from CSV later under production pressure instead of designing correctly early.

Summary

  • MySQL does not have a native relational array type like document databases.
  • A child table is the most robust and queryable design for most workloads.
  • JSON columns are useful for flexible, semi-structured array data.
  • CSV storage is usually a short-term workaround, not a durable model.
  • Pick the approach based on query needs, integrity rules, and maintenance cost.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.