9 min read

TONL vs SQLite for Local Structured Data and LLM Apps

SQLite gives you a query engine in a file; TONL gives you queries, schema, streaming, and an LLM-ready text format with zero dependencies. Here's how to choose between them.

By JSON to TOON Team

Use SQLite when you need multi-table relational queries, ACID transactions, or concurrent writes. Use TONL when your data must also feed directly into an LLM prompt — TONL saves 32–50% of tokens versus JSON, ships a SQL-like query API with sub-0.1ms indexed lookups, and streams 50GB-plus files in under 100MB of memory, all with zero runtime dependencies.

What Problem Does Each Tool Actually Solve?

SQLite and TONL look similar from a distance — both let you store structured data locally without a server, query it with filter expressions, and validate schemas. But they were designed for fundamentally different constraints, and the difference shows up the moment you need to feed your data to a language model.

SQLite is a mature, battle-tested relational database engine. Its storage format is a binary B-tree file optimized for concurrent reads and writes, multi-table joins, and ACID-compliant transactions. It has been embedded in browsers, phones, and operating systems for over two decades. It knows nothing about token counts, LLM context windows, or prompt efficiency.

TONL (Token-Optimized Notation Language) is a text-first format designed from the ground up to be both machine-queryable and LLM-promptable. The same bytes you store on disk are the bytes you drop into a prompt. There is no serialization step between "query the data" and "feed the data to the model." That property is architecturally significant for AI applications: the fewer transformation steps between storage and the prompt, the less room for bugs and the less latency in your pipeline.

For a deeper look at TONL's design, see the introduction to TONL and the architecture overview.

Side-by-Side: The Same Query in TONL and SQL

To make the comparison concrete, consider a local product catalog — 500 records, each with an ID, name, category, price, and availability flag. You want all in-stock electronics priced under $100. Here is the same operation expressed as a TONL query and as the equivalent SQLite SQL:

// === The dataset ===

// SQLite — stored as a binary .db file; you must open a connection,
// run the query, fetch rows, then serialize to text before prompting an LLM.

CREATE TABLE products (
  id       INTEGER PRIMARY KEY,
  name     TEXT,
  category TEXT,
  price    REAL,
  in_stock INTEGER  -- 1 = true, 0 = false
);

SELECT id, name, price
FROM   products
WHERE  category = 'electronics'
  AND  in_stock = 1
  AND  price < 100
ORDER BY price ASC;

// Then: serialize result rows to JSON/CSV/text → insert into prompt.
// Extra step. Extra code. Extra latency.


// === TONL — stored as a .tonl text file ===
// The stored format IS the prompt format. No intermediate serialization.

products[500]{id:u32, name:str, category:str, price:f32, in_stock:bool}:
  1, "Wireless Mouse",    electronics, 29.99, true
  2, "HDMI Cable 2m",     electronics, 12.49, true
  3, "4K Monitor 27in",   electronics, 349.00, true
  4, "USB-C Hub",         electronics, 49.99, true
  // ... 496 more rows ...

// TONL query — same intent, runs in the same process, result is TONL text
QUERY products
  SELECT id, name, price
  WHERE  category = "electronics"
    AND  in_stock = true
    AND  price < 100
  ORDER BY price ASC

// Result is already TONL — paste directly into the LLM prompt.

The TONL query result is TONL text. You pass it straight to the model. With SQLite you have an extra step: fetch rows as a language-native data structure, then serialize that structure to whatever text format you want in the prompt. For a one-off script that step is trivial. For a production pipeline called thousands of times per day, it is a maintenance surface.

TONL vs SQLite: Full Feature Comparison

DimensionTONLSQLite
Storage formPlain text (.tonl) — human-readable, diff-friendly, version-control friendlyBinary B-tree file (.db / .sqlite) — not human-readable, not diff-friendly
Query engineBuilt-in SQL-like query API; sub-0.1ms indexed lookups; filtering, projection, aggregationFull SQL (SELECT, JOIN, GROUP BY, subqueries, CTEs, window functions); mature and battle-tested
LLM-promptable directlyYes — stored format is the prompt format; no serialization stepNo — binary format must be serialized to text (JSON, CSV, etc.) before prompting
Token efficiency vs JSON32–50% fewer tokens; up to ~60% with optional compression layersNot applicable — binary format; token cost depends on how you serialize the query result
Schema & typesType hints (u32, str, bool, f32); schema validation at parse time; auto TypeScript generationColumn types (INTEGER, TEXT, REAL, BLOB, NUMERIC); strict mode optional; no TypeScript generation
Runtime dependenciesZero (tonl npm package is self-contained)Requires a SQLite binding (better-sqlite3, sql.js, node:sqlite in Node 22+)
Streaming large files50GB+ files in under 100MB memory via built-in streaming APIStreaming via cursor/iterator; memory use depends on result set size
CRUD & change-trackingFull CRUD with change-tracking and rollback built into the packageFull CRUD; transactions and rollback via SQL; no built-in change-tracking log
Multi-table joinsNot supported — single-block or multi-block, no foreign-key joinsFull JOIN support (INNER, LEFT, CROSS, etc.)
Concurrent write accessSingle-writer model (text file); concurrent reads are fineWAL mode supports one writer + multiple concurrent readers; full transaction isolation
Best workloadSingle-process read-heavy apps where data feeds LLM prompts; AI pipelines; edge/serverlessRelational apps; multi-table normalized schemas; heavy write workloads; non-LLM use cases

Where SQLite Wins: Be Honest About the Trade-offs

SQLite is a better choice in several situations, and pretending otherwise would be misleading.

Multi-table relational data. If your schema has foreign keys, normalized tables, and queries that join across them, SQLite's full SQL engine is irreplaceable. TONL has no JOIN operation. You could flatten the data before writing it to TONL, but that trades normalization for convenience — a decision only you can make based on your write patterns.

High write throughput. TONL uses a text file as its backing store, which means write operations involve reading, modifying, and rewriting file sections. SQLite's B-tree engine is purpose-built for efficient random writes and WAL-mode concurrency. If your application writes thousands of rows per second from multiple processes, SQLite wins by a large margin.

Existing SQL tooling. The ecosystem around SQLite is vast: DB Browser for SQLite, Beekeeper Studio, Datasette, countless ORMs and query builders. If your team already operates in that ecosystem, the switching cost to TONL is real.

Non-LLM applications. If your local data never touches an LLM, TONL's primary differentiator — LLM-ready text representation — does not matter to you. SQLite is the more mature general-purpose choice.

Where TONL Wins: LLM Apps, Edge, and Zero-Dependency Pipelines

TONL's advantages are most pronounced in a specific class of application: one where the same dataset is both queried programmatically and fed to a language model.

Prompt efficiency. Every token you remove from a prompt reduces cost and increases the headroom for instructions, retrieved context, and model reasoning. According to tonl.dev, TONL saves 32–50% of tokens versus JSON. Even at the conservative end, cutting input tokens by a third on every call adds up quickly in a high-volume pipeline. The optional compression layers can push savings to roughly 60% for datasets with repeated strings, sequential timestamps, or small-range integers.

No serialization gap. The TONL query result is TONL text. You pass the output of QUERY products SELECT ... WHERE ... directly into the prompt. There is no intermediary step of calling JSON.stringify, writing a CSV serializer, or building a markdown table. The format that lives on disk is the format the model reads.

Edge and serverless environments. Zero runtime dependencies means TONL installs as a single npm package with no native bindings, no platform-specific build step, and no .node file that causes deployment failures on Lambda, Cloudflare Workers, or Vercel Edge Functions. SQLite bindings like better-sqlite3 require native compilation and cannot run in all serverless environments.

Version-controlled datasets. TONL files are plain text. They diff cleanly in Git, can be reviewed in pull requests, and are editable in any text editor. SQLite binary files do not diff, which makes reviewing data changes in code review impossible without a specialized tool.

For the TOON vs TONL comparison — including when to use the lighter-weight TOON format instead — see the TOON vs TONL guide.

A Practical Decision Framework

The choice between TONL and SQLite is not about which tool is technically superior — it is about which constraints matter most for your specific application. Work through these questions:

  • Does the data need to go into an LLM prompt? If yes, TONL's native text representation removes an entire transformation layer and saves 32–50% of tokens.
  • Do you need multi-table JOINs? If yes, SQLite. TONL has no JOIN support.
  • Are you on a serverless or edge platform? If yes, TONL's zero-dependency model is significantly easier to deploy.
  • Do you have high concurrent write throughput? If yes, SQLite's WAL engine handles this better than a text file.
  • Does your team need to review data changes in Git? TONL files are diffable; SQLite binary files are not.
  • Do you need 50GB-plus file streaming? TONL's built-in streaming API handles this in under 100MB of memory per tonl.dev. SQLite can handle large files too, but through a different cursor-based mechanism.

For many AI applications the answer is "TONL for the LLM-facing data layer, SQLite (or Postgres) for the relational business logic layer." They are not mutually exclusive. A common pattern is to query your SQLite database, project the columns you need, and serialize that slice to TONL for the prompt — combining the relational power of SQL with the token efficiency of TONL for the context window.

To see how TONL looks compared to simpler formats like TOON, read the introduction to TOON before committing to the fuller TONL stack.

Frequently Asked Questions

Should I use TONL or SQLite for local data?

Use SQLite when you need multi-table relational queries, ACID transactions, or concurrent writes from multiple processes. Use TONL when you also need to feed the data directly into an LLM prompt without a serialization step — TONL saves 32–50% of tokens versus JSON and ships a SQL-like query API with sub-0.1ms indexed lookups.

Can TONL replace a database for small LLM apps?

For single-process read-heavy apps where the data also needs to go into an LLM context, yes. TONL's built-in query API supports filtering, field selection, and aggregation with sub-0.1ms indexed lookups. It also supports CRUD with change-tracking and rollback. For multi-table joins, concurrent writes, or large write-heavy workloads, SQLite remains the better choice.

How does TONL handle large files compared to SQLite?

TONL's streaming API processes files larger than 50GB in under 100MB of memory, according to tonl.dev. SQLite also handles large databases well but requires loading query results into memory and then re-serializing to a text format before feeding an LLM. TONL eliminates that re-serialization step since the stored format is already LLM-ready.

Does TONL have schema validation like SQLite column types?

Yes. TONL supports type hints (u32, str, bool, f32) that enable schema validation at parse time and automatic TypeScript type generation. The type hints add roughly 20 tokens to a block header but keep the total representation 32% smaller than untyped JSON, according to tonl.dev.

Is TONL suitable for production use without a database?

TONL v2.5.2 ships with 2,300-plus tests passing and zero runtime dependencies, as documented on tonl.dev and the GitHub repository. Its query API, streaming, schema validation, and CRUD with change-tracking are all covered by that test suite. Whether it replaces a database depends on your write patterns and whether you need relational joins — not on stability.

Recommended Reading

TONLSQLiteQuery APILocal DataData PlatformLLM