Browse by type
SlothDB is an embedded SQL database that runs everywhere: on your laptop, on a server, and in the browser. Built from scratch. Up to 5x faster where it counts.
Website · Playground · Discord · Blog · Docs · Benchmarks · Python · SQL Guide
A from-scratch C++ embedded database, measured head to head against DuckDB on the industry-standard analytical benchmark.

ClickBench is the standard
analytical-database benchmark: 43 queries over a 100M-row hits Parquet
dataset. Both engines run the 43 official queries directly against the
same file on the same machine, 3 trials each, fastest of 3 reported.
SlothDB completes 40 of the 43 and is faster than DuckDB on 29 of
those 40, geomean 1.24x. Three queries it does not finish inside the
120-second cap: Q19 and Q33 (very high-cardinality GROUP BY) and Q29
(a regex GROUP BY). Those are real gaps, shown here, not hidden.

The queries SlothDB loses are drawn in red, not dropped. High-cardinality
two-column GROUP BY (Q31 at 0.22x, Q32 at 0.11x) is the weak spot, and
because of it the total wall time across the 40 comparable queries is
close: SlothDB 71s, DuckDB 65s. SlothDB takes the per-query majority and
the geomean; DuckDB takes the sum, since a few high-cardinality
aggregations are slow.
Measured on a 6-core laptop (Ryzen 5 5600U, 15 GB RAM, Windows 11) against DuckDB. This is a local run, not an official ClickBench submission; ClickBench timings are hardware-specific, so reproduce it on yours:
python bench/clickbench/official_bench.py
Raw per-query times: bench/clickbench/official_results.md.
Type .ask at the slothdb> prompt. A rules parser handles catalog questions and common English shapes in under 10 ms with no model. Anything else falls through to a local Qwen2.5-Coder (0.5B for simple, 1.5B for analytic; lazy-downloaded on first use under -DSLOTHDB_ASK_MODEL=ON), which speaks 29 natural languages: English, Chinese, Spanish, French, German, Japanese, Korean, Russian, Arabic, Portuguese, Italian, Hindi, and more. Every generated statement is shown before it runs. Nothing leaves the machine. Set SLOTHDB_ASK_CONFIRM=1 to add a [Y/n] prompt before each run.
| tier | what | cost | covers |
|---|---|---|---|
| 1 | Rules parser (default) | sub-10 ms, no model | catalog, COUNT/SUM/AVG/GROUP BY/TOP-N, file-source |
| 2 | Local Qwen 2.5-Coder 0.5B Q4_K_M | ~200 ms, ~310 MB | open-ended SELECT/GROUP BY/filter |
| 3 | Local Qwen 2.5-Coder 1.5B Q4_K_M | ~500 ms, ~986 MB | window functions, ranking within groups, LAG/LEAD, joins |
Both model tiers download lazily in parallel on first .ask (total ~1.3 GB). Router is a pure function of the question: no LLM call involved in routing. Cumulative / running / moving aggregates refuse cleanly (engine gap, not model gap). Full spec, router signals, refusal policy: docs/ASK.md.
In your browser - no install, no account: slothdb.org/playground. Full SlothDB compiled to WebAssembly, with a pre-loaded 1,000-row demo CSV + matching Parquet to compare format performance. Files you add stay on your machine.
In Python - CPython 3.8+ on Linux / macOS / Windows (see latest release for published wheel tags; falls back to source build if no wheel matches):
pip install slothdb
python -c "import slothdb; slothdb.demo()"
That generates a 100 000-row CSV, runs three queries, and prints the side-by-side with DuckDB shown above. No files to find, no setup.
# Your own files, same API. pandas round-trip in two lines:
import slothdb, pandas as pd
db = slothdb.connect()
df = db.sql("SELECT region, SUM(revenue) AS rev FROM 'sales.parquet' GROUP BY region").fetchdf()
In Node/JS - npm install @slothdb/wasm:
import { SlothDB } from '@slothdb/wasm';
const db = await SlothDB.create();
const { columns, rows } = db.query("SELECT 1 AS n");
In the shell - download or build from source; then slothdb analytics.slothdb for a persistent single-file DB.
hits dataset and is faster than DuckDB on 29 of the 40 queries it completes, geomean 1.24x (see the ClickBench section above). The query engine gained radix-partitioned aggregators for high-cardinality GROUP BY, a bounded hash-table COUNT path, parquet decode improvements (zero-copy VARCHAR, batched RLE unpack), and TopN pushdown into the aggregate.getenv_s is replaced with std::getenv, and the vendored snappy source is committed so CI can build it.bench/clickbench/. All 43 ClickBench queries verbatim from the ClickBench repo, plus the runner and a chart generator. python bench/clickbench/official_bench.py reproduces the head-to-head against DuckDB.424 tests pass.
ROUND(AVG(x)), AVG(x) + 1, SUM(x) / COUNT(*), CAST(SUM(y) AS DOUBLE) and other shapes that wrap an aggregate inside a scalar function or arithmetic used to throw "Function execution for: AVG". The planner now walks the whole expression tree and hoists every aggregate it finds, no matter how deep, so you can write the SELECT list the way you'd write it in DuckDB or Postgres.ORDER BY by aggregate alias works. SELECT region, COUNT(*) AS cnt FROM ... GROUP BY region ORDER BY cnt DESC used to default the sort to column 0 and silently sort by region. PhysicalOrderBy now precomputes per-row order keys via the expression executor whenever any clause isn't a plain column ref. Both the full-sort and top-N heap paths use the precomputed keys.AVG(x) + 1 would lose the +1 and AVG(x) / COUNT(*) would return inf, both because the typed arithmetic kernel reinterpreted operand bytes instead of converting them. Now coerces both operands to the result type with typed fast paths (int to double, int to bigint, float to double).bench/ directory at the repo root. All 43 ClickBench queries verbatim from ClickHouse/ClickBench plus a 16-query mixed suite, behind a generic Python runner. Reproducible side-by-side timing against DuckDB. See bench/README.md.408 tests, 131,537 assertions, green on Windows / Linux / macOS.
read_parquet glob in the browser. SELECT * FROM '/data/shard_*.parquet' fans out across every match in the playground's MEMFS instead of failing with "Cannot open Parquet". Same path the native CLI has had for a while.std::thread spawn is now routed through a HWThreads() helper that returns 1 under __EMSCRIPTEN__ && !__EMSCRIPTEN_PTHREADS__ and runs the worker inline.Access-Control-Allow-Origin. FROM 'https://host/file.parquet' works in the playground for any host.ORDER BY ... LIMIT N on Parquet. Bounded-heap operator instead of full sort then truncate. 10M-row ORDER BY q DESC LIMIT 10: 420,344 ms to 119 ms.slothdb_column_int32_buffer / int64_buffer / double_buffer / varchar_buffer / validity_buffer. The Python wrapper reads one buffer per column instead of two ctypes calls per cell. SELECT 2 columns x 10M rows: 46 s to 16 s.string_t emit + lazy QueryResult. Result chunks stay alive until the user pulls them; fetchnumpy() and fetchdf() skip the per-cell Value boxing entirely..ask natural-language sub-REPL. Rules parser in every build (sub-10 ms, no model). Optional local Qwen2.5-Coder GGUF fallback under -DSLOTHDB_ASK_MODEL=ON: 0.5B and 1.5B tiers picked by a deterministic keyword router. 29 languages. Generated SQL is shown before it runs. docs/ASK.md.CREATE LIVE VIEW with incremental CSV append. Useful when a dashboard tails a log that keeps growing.-DSLOTHDB_EDGE=ON) for sub-MB WASM bundles under Cloudflare Workers' 1 MB cap.Per-commit history with bench deltas in CHANGELOG.md.
SlothDB is an embedded analytical database in C++20. You link it into your application (or run the shell) and point SQL at files on disk. No server process, no import step, no "load the extension first." That's the same model as DuckDB and SQLite, but the defaults are different.
-- No CREATE TABLE. No COPY FROM. Just point at the file.
SELECT department, COUNT(*), AVG(salary)
FROM 'employees.parquet'
WHERE hire_year >= 2020
GROUP BY department
ORDER BY AVG(salary) DESC;
-- Local, HTTP(S), or public S3 - same SQL.
SELECT region, SUM(revenue) FROM 'https://host/data.csv' GROUP BY region;
SELECT * FROM 's3://public-bucket/events.parquet';
DuckDB is great and a lot of users should keep using it. SlothDB overlaps on the embedded-columnar-SQL story but makes different default choices. Four of those choices are what we've heard asked for most:
CREATE LIVE VIEW caches a query result and, for the common SELECT * FROM 'file.csv' shape on CSV/TSV, appends only the new bytes on the next query. Useful when a dashboard tails a log that keeps growing.-DSLOTHDB_EDGE=ON build (CSV/JSON/Parquet only) targets Cloudflare Workers' 1 MB script budget; the full WASM bundle is around 1.3 MB.ErrorCode::TABLE_NOT_FOUND = 2000 does not shift between releases; bindings built against 0.1.x keep working.Same idea as DuckDB otherwise: embedded, columnar, vectorized, query files directly. Head-to-head on our bench:
| SlothDB | DuckDB | |
|---|---|---|
| 5-query warm JOIN batch (1 M × 1 K) | 138 ms | 540 ms |
| Live |
$ claude mcp add slothdb \
-- python -m otcore.mcp_server <graph>