SQL Boy
TutorialsPlayground

Format & Validate

SQL FormatterSQL MinifierSyntax Validator

Convert

JSON to SQLCSV to SQLSQL to JSONRegex to SQLExcel to SQLSQL Dialect Convertersoon

Visualize

ER Diagram GeneratorSQL Schema Diff

Generate

SQL Mock Data Generator

Analyze

SQL Query ExplainerSQL Query Analyzer

Workflow hubs

FormattingConversionSchemaAnalysis
View all tools
Daily ChallengeInterviewsCheat SheetBlog

© 2026 SQL Boy. Built for developers.

ContactPrivacyTermsAI Usage PolicyEditorial StandardsAboutRSS Feed

New conversation

What can I help with?

Ask about this page, get a hint on a challenge, or explore SQL concepts.

I know what's on this page and can give answers grounded in SQL Boy content.

Current page

Generating Test Data With Sql

/blog/generating-test-data-with-sql

Usage status will load after login
SQL Boy
TutorialsPlayground

Format & Validate

SQL FormatterSQL MinifierSyntax Validator

Convert

JSON to SQLCSV to SQLSQL to JSONRegex to SQLExcel to SQLSQL Dialect Convertersoon

Visualize

ER Diagram GeneratorSQL Schema Diff

Generate

SQL Mock Data Generator

Analyze

SQL Query ExplainerSQL Query Analyzer

Workflow hubs

FormattingConversionSchemaAnalysis
View all tools
Daily ChallengeInterviewsCheat SheetBlog
Back to Blog
Published 2026-01-14
Updated 2026-04-20
7 min read

Generating Massive Test Data with SQL (No Scripts Required)

sqlitetestingctesperformancedata-generation

Author

SQL Boy Team

Editorial Team at SQL Boy

This article is maintained as part of SQL Boy's hands-on SQL library.

We aim to keep examples runnable, call out dialect differences, and revise unclear sections over time.

Read editorial standardsAbout SQL BoyRequest a correction

If a result or dialect note looks wrong, email [email protected] with the article URL and the section you want reviewed.

You've designed a great schema and wrote a clever query. But testing it on 5 rows isn't enough. You need to know how it performs with 10,000 or 100,000 rows.

Does this mean you need to write a Python script to generate CSVs and import them?

No! You can generate massive, realistic datasets purely with SQL using Recursive CTEs.

Recursive number series expanding into a large dataset
Recursive number series expanding into a large dataset

A Real Business Scenario: Reproducing a Slow Query Before Release

Suppose you are about to ship a reporting page that groups users by signup month and activity bucket.

The query looks fine on your laptop with 20 rows. The problem is that production will have:

  • tens of thousands of users
  • uneven signup dates
  • skewed activity distributions

If you test only on tiny hand-written samples, you miss the real risks:

  • an index that looks unnecessary until row counts grow
  • a sort that spills once the result is wide enough
  • pagination or charts that behave badly with realistic volume

Synthetic SQL-generated data helps you pressure-test the exact schema and query shape you already have, without context-switching into a separate scripting tool for every experiment.

The Magic of WITH RECURSIVE

A Recursive CTE (Common Table Expression) allows a query to refer to itself. This is perfect for generating series (1, 2, 3...) which can then be transformed into data.

Generating a Number Series

Here is the "Hello World" of test data:

WITH RECURSIVE generate_series(value) AS (
  SELECT 1  -- Initial value
  UNION ALL
  SELECT value + 1 FROM generate_series
  WHERE value + 1 <= 10 -- Stop condition
)
SELECT value FROM generate_series;

Generating Random Data

Once you have a sequence of numbers, you can use SQLite's math functions to generate random attributes.

  • Random Integer (1-100): ABS(RANDOM()) % 100 + 1
  • Random Date: DATE('now', '-' || (ABS(RANDOM()) % 365) || ' days')
  • Random String: Use CASE or substring logic (though SQL is a bit limited here, we can fake it).

A Common Mistake: Random But Unrealistic Data

It is easy to generate random rows that technically fill a table but fail to simulate the production patterns you care about.

For example:

  • perfectly uniform dates, even though real traffic spikes on weekdays
  • totally random foreign keys, even though a few entities dominate activity
  • complete data with no NULLs, even though production imports are messy

That kind of data is fine for smoke tests, but weak for debugging query plans or UI behavior. Good test data should reflect the constraints and weirdness of the real system, not just the column types.

If your production data is skewed, sparse, or seasonal, your synthetic dataset should be too.

Interactive Example: Creating 1,000 Users

Let's generate a users table with 1,000 rows. We'll give them:

  • An ID
  • A random "Group" (A, B, C, D, E)
  • A random Signup Date within the last year
  • A random Activity Score (0-5000)
Interactive SQL
Loading...

Why random data matters

Generating random data is crucial for:

  1. Index Testing: An index behaves differently with 10 rows vs 100,000 rows.
  2. Query Optimization: You can't see "Slow Query" warnings if your query runs in 0.001ms on an empty table.
  3. UI Stress Testing: See how your frontend handles pagination, sorting, and large numbers.

Boundary and Performance Notes

Recursive generation is powerful, but it has limits.

  • Large recursive CTEs can be slower than bulk-loading from a file if you need millions of rows across many related tables.
  • Pure randomness is hard to reproduce unless you fix the generation logic carefully. If a failing case matters, deterministic fixture patterns are easier to debug than constantly changing random values.
  • If tables have foreign keys, uniqueness rules, or realistic correlations, you often need staged inserts instead of one giant statement.
  • Massive local seed scripts are useful for testing, but they should stay out of production migration paths unless you explicitly want them there.

In practice, SQL generation is best for quick performance experiments, reproducible demos, and lightweight fixtures that stay close to the schema under test.

Generating Dates

A common requirement is generating a continuous range of dates (e.g., "every day in 2024").

WITH RECURSIVE dates(date) AS (
  SELECT '2024-01-01'
  UNION ALL
  SELECT DATE(date, '+1 day')
  FROM dates
  WHERE date < '2024-12-31'
)
SELECT * FROM dates;

This is invaluable for "Filling the gaps" in charts where you have no sales for a specific day but still want the day to appear on the X-axis.

When NOT to Generate Data This Way

Do not force everything into recursive SQL if another method fits the job better.

Examples:

  • you need realistic names, addresses, or localized content
  • you need cross-table correlations that are easier to express in application code
  • you are testing privacy-sensitive workflows and should instead mask a production-like extract

Recursive SQL is great when the point is volume, sequence generation, or quick schema-aligned fixtures. It is less ideal when realism depends on domain-specific logic or external libraries.

Official References

  • SQLite WITH clause documentation for recursive CTE syntax, rules, and caveats.
  • SQLite core functions for random() and related built-in behavior used in synthetic data generation.
  • PostgreSQL set-returning functions for a contrasting major-engine reference where generate_series is built in rather than simulated recursively.

Tool Workflow

Use tools when SQL-generated test data should become a broader seeding workflow

Recursive SQL is powerful for synthetic data, but browser tools are often faster when you need schema-aware inserts, fixture conversion, or quick non-production data setup.

Mock Data Generator

Generate schema-aware INSERT statements quickly when you want realistic seed data without hand-writing recursive SQL for every table.

Schema Diff

Compare schema versions before regenerating fixtures so large seed scripts do not drift away from the tables they target.

Schema Design Workflow Hub

Use the broader schema path when data generation, table design, and migration review belong in the same workflow.

Related Articles

  • Mastering CTEs: Writing Cleaner, Better SQL for the underlying WITH mental model behind recursive series generation.
  • Optimizing Large Dataset Queries for what to test once the synthetic row counts are finally large enough to reveal real bottlenecks.
  • Data Masking and Anonymization Techniques in SQL for the cases where you should transform production-like data safely instead of generating everything from scratch.

Conclusion

You don't need external tools to populate your database. With SQLite's WITH RECURSIVE, you have a powerful factory for synthetic data right at your fingertips.

Next time you need to test performance, don't guess—generate!

Share this article:

Topic Path

This article belongs to a larger cluster

If this page matches the problem you are working on, jump to the topic hub to see the surrounding articles in the same path instead of treating this as a one-off post.

Data Prep

Data cleaning, staging, and SQL-ready inputs

Use this path when the hard part is turning messy files, semi-structured payloads, or staging tables into something you can query with confidence.

Open topic hub

Related Articles

sqliteperformance

Full-Text Search in SQLite with FTS5

Go beyond LIKE queries. Learn how to build lightning-fast full-text search using SQLite FTS5 virtual tables with BM25 relevance ranking.

Read more
sqliteperformance

Simulating Materialized Views in SQLite with Triggers

SQLite doesn't support materialized views, but you can build them yourself! Learn how to use triggers to create high-performance cached summary tables.

Read more
performance

Essential SQL Optimization Techniques for Faster Queries

Is your query taking forever? Learn proven optimization techniques: indexing strategies, JOIN optimization, subquery rewrites, and execution plan analysis.

Read more
Previous

SQLite UPSERT with ON CONFLICT: INSERT or UPDATE in One Query

Next

Building a Weighted Search Engine with Pure SQL

Comments

© 2026 SQL Boy. Built for developers.

ContactPrivacyTermsAI Usage PolicyEditorial StandardsAboutRSS Feed