Apache DataSketches
Bring Apache DataSketches into DuckDB.
On this page
Technical Overview
Billion-row aggregates on a bounded memory budget
Apache DataSketches is the production-grade C++ library behind streaming approximate aggregates; this extension makes it callable from DuckDB SQL. The value proposition is billion-row aggregates with bounded memory and merge-friendly state: every sketch serializes to a portable BLOB you can store in a column, ship between processes, and merge later without rescanning the source data. The trade is approximate answers, not exact ones.
How sketches work
A sketch is a small, fixed-size summary of a stream. Instead of keeping every value (or every distinct value) the way an exact COUNT(DISTINCT) or a full sort does, a sketch maintains a bounded amount of probabilistic state in a single pass and answers a specific statistical question โ distinct count, quantile, or heavy-hitter frequency โ from that state alone. This extension is a thin DuckDB wrapper around the Apache DataSketches C++ library: each family registers a one-pass aggregate that builds the sketch, a typed BLOB column type that carries the serialized state, and reader scalars that decode it.
- โข Bounded memory, single pass: A sketch's footprint is fixed up front โ it does not grow with the number of input rows. A billion-row distinct count that would otherwise need a sort plus a large hash table becomes a constant-memory aggregate computed in one scan, with mathematically grounded error bounds rather than ad-hoc approximation.
-
โข
The size โ accuracy knob: Each family takes a sizing parameter โ
lg_kfor the cardinality sketches (HLL/CPC/Theta),Kfor the quantile sketches (KLL/TDigest/REQ/classic),lg_max_map_sizefor Frequent Items. Larger means tighter error and a bigger sketch; smaller means a cheaper sketch and looser bounds. For example, an HLL atlg_k = 12is roughly 4 KB at about ยฑ1.6% standard error. You pick the point on that curve that matches your accuracy budget. - โข State is mergeable without rescanning: The serialized sketch is the durable, composable unit. Build one sketch per partition or per day, persist it as a typed BLOB column, then union those sketches into any rolling window (per-day โ 7d/30d, per-shard โ global) without ever touching the original rows again. Theta sketches go further, supporting approximate union, intersection, and A-not-B set algebra over sketch state alone โ the basis for funnel, retention, and churn analysis.
- โข Portable across systems: The serialization format matches the Java reference implementation โ the same format consumed by Druid, Pinot, BigQuery, and Spark integrations. A sketch built in DuckDB and written to Parquet can be merged inside another DataSketches-aware system, and vice versa, with no re-aggregation across the boundary.
When to reach for sketches vs. exact aggregates
Sketches are approximate by design. The trade-off is bounded memory, mergeable state, and order-of-magnitude faster aggregation against a small, predictable error. They are not a replacement for exact answers when policy demands them.
Reach for sketches when
Input is too large for comfortable exact aggregation, you want to persist state and merge it later (per-day โ rolling windows, per-shard โ global), or you need cross-system interop (Druid, Pinot, Spark, BigQuery) on the same sketch BLOB. Anything that must survive a process restart or roll up across shards is a natural fit.
Stick with exact aggregates when
Your data fits and exact answers are policy (billing, audits, reconciliation). DuckDB's built-in COUNT(DISTINCT) and quantile_cont are vectorized and fast at most analytical scales โ measure before reaching for a sketch.
Reach for this extension over DuckDB's approx built-ins when
DuckDB ships approx_count_distinct (HLL) and approx_quantile (TDigest), but those expose only the final estimate โ the sketch state is discarded. Use this extension when you need the sketch as reusable state: to persist it as a column, merge sketches later, tune the size parameters, or interoperate with the wider Apache DataSketches ecosystem.
Deep Dive
Technical Details
DuckDB โ Apache DataSketches
Mergeable, persistable streaming sketches โ distinct counts, quantiles, and heavy hitters โ from the Apache DataSketches library.
A billion-row distinct count, persisted
A naive COUNT(DISTINCT user_id) over a billion-row event table is a sort plus a hash table โ minutes of work, gigabytes of memory, a fresh scan every time someone asks again. With a Theta sketch, itโs a fixed-memory aggregate you build once per partition and roll up on demand:
-- One pass, per-day sketch state, persisted as a typed BLOB columnCREATE TABLE daily_uniques ASSELECT date_trunc('day', ts) AS day, datasketch_theta(12, user_id) AS sketch -- ~64 KB per dayFROM eventsGROUP BY 1;
-- Rolling 7-day uniques: union the per-day sketches, no rescanSELECT datasketch_theta_estimate( datasketch_theta_union(sketch) ) AS uniques_last_7dFROM daily_uniquesWHERE day >= CURRENT_DATE - 7;
-- Retention: users active in BOTH last week AND this weekWITH last_week AS (SELECT datasketch_theta_union(sketch) AS s FROM daily_uniques WHERE day BETWEEN CURRENT_DATE - 14 AND CURRENT_DATE - 7), this_week AS (SELECT datasketch_theta_union(sketch) AS s FROM daily_uniques WHERE day >= CURRENT_DATE - 7)SELECT datasketch_theta_estimate( datasketch_theta_intersect(last_week.s, this_week.s) ) AS retainedFROM last_week, this_week;The first query touches the source rows once. The next two โ and any other window, set difference, or intersection you care to ask โ operate on the sketch state alone.
Sketches are not a faster way to get exact answers. They give bounded-memory, mergeable, approximately correct aggregates with a small, predictable error. The default HLL sketch (lg_k = 12) is roughly ยฑ1.6% on distinct counts; the default KLL (K = 200) puts quantile error at roughly ยฑ1.5% rank. Pick the size knob to match your accuracy budget โ and donโt reach for sketches when policy demands exact (billing, audits, reconciliation).
Architecture
This extension is a thin DuckDB wrapper around the Apache DataSketches C++ library. Every sketch family registers:
- An aggregate (
datasketch_kll,datasketch_hll,datasketch_theta, โฆ) that builds the sketch in one pass over input values. - A typed BLOB column type (
sketch_kll_double,sketch_hll,sketch_theta, โฆ) โ a strict superset ofBLOBcarrying the serialized sketch. - Reader scalars (
datasketch_*_quantile,datasketch_*_estimate,datasketch_*_cdf, โฆ) that decode the BLOB and return values without rebuilding from rows. - Mergers (
datasketch_hll_union,datasketch_theta_union, โฆ) โ aggregates over a sketch column that produce a rolled-up sketch.
The serialization format is the same one the Java reference implementation writes, which is the format Druid, Pinot, BigQuery, and Spark integrations consume. A sketch built in DuckDB and written to Parquet can be merged inside Druid or vice versa โ no re-aggregation across systems.
Choosing a sketch
The right sketch depends on what youโre estimating and which constraint binds first โ read latency, sketch size, or set algebra.
Distinct counts (cardinality)
| Family | When to pick | Notes |
|---|---|---|
| HLL | Default โ fastest reads, broad cross-system interop | lg_k 4โ21; 12 is a good default (~4 KB sketch) |
| CPC | Storage-bound โ many sketches at rest | ~40% smaller than HLL at the same accuracy |
| Theta | You need union, intersection, or A-not-B (funnel, retention, churn) | Only family with full set algebra |
Quantiles
| Family | When to pick | Notes |
|---|---|---|
| KLL | Default โ best accuracy/size balance | K = 200 is a sensible production starting point |
| TDigest | p99 / p999 SLO reporting | FLOAT/DOUBLE only; tail-accurate |
| REQ | Skewed distributions | Error scales with rank, not fixed |
| Quantiles | Compatibility with classic pipelines | Use KLL for new work |
Frequency
A single family โ Frequent Items โ for top-K heavy hitters with confidence-bounded frequencies. Use 'NO_FALSE_POSITIVES' to get items that are definitely heavy, 'NO_FALSE_NEGATIVES' to get every candidate that might be.
Compared to alternatives
- DuckDBโs built-in
approx_count_distinctandapprox_quantileโ both ship in DuckDB and use HLL / TDigest internally, but expose only the final estimate. Reach for this extension when you need the sketch as state โ to persist it as a column, merge later, tune sketch parameters (lg_k,K,map_size), or interoperate with other Apache DataSketches consumers. - Exact aggregates (
COUNT(DISTINCT),quantile_cont,mode) โ the right answer when policy demands exact and your data fits. DuckDBโs vectorized exact aggregates are fast at most analytical scales; measure before reaching for an approximate sketch. - Reservoir / streaming aggregates rolled by hand โ sketches give you the same bounded-memory streaming property plus mathematically grounded error bounds plus mergeability across processes. For anything that needs to survive a process restart or roll up across shards, the sketch is the better primitive.
- Druid / Pinot / BigQuery sketch columns โ those systems use the same serialization format. If youโre already producing sketches there, this extension lets DuckDB read and merge them; if youโre building them in DuckDB, downstream OLAP systems can consume the BLOBs directly.
Persistence and portability
Every sketch round-trips through a typed BLOB column (sketch_kll_double, sketch_hll, sketch_theta, etc.) thatโs a strict superset of BLOB. Sketches written into a Parquet file can be read back into any DuckDB instance with this extension loaded, or into any other DataSketches-aware system, and merged without a re-scan. Aggregate functions accept either raw input values or an existing sketch column, so incremental rollup is just another SELECT.
Install
INSTALL datasketches FROM community;
LOAD datasketches;
Quick Start
Approximate distinct users with HLL (lg_k = 12 โ ~4 KB sketch, ~1.6% error)
SELECT datasketch_hll_estimate(datasketch_hll(12, user_id)) AS distinct_users
FROM events;
p50 / p95 / p99 latency from a KLL quantile sketch
WITH agg AS (
SELECT datasketch_kll(200, latency_ms) AS sketch FROM requests
)
SELECT
datasketch_kll_quantile(sketch, 0.50, true) AS p50,
datasketch_kll_quantile(sketch, 0.95, true) AS p95,
datasketch_kll_quantile(sketch, 0.99, true) AS p99
FROM agg;
Reference
Extension Contents
Quick reference to all available functions and settings organized by category.
| Name | Type | Description |
|---|---|---|
|
CPC
Compressed Probability Counting โ distinct-counting at roughly 40% the size of HLL at the same accuracy, traded against slower serialization. Choose this when you store many sketches at rest. |
||
| datasketch_cpc() | Aggregate input values into a Compressed Probability Counting sketch โ a distinct-count sketch that's roughly 40% smaller than HLL at the same accuracy, traded against slower serialization. | |
| datasketch_cpc_describe() | Return a string representation of the sketch | |
| datasketch_cpc_estimate() | Read the estimated distinct count from a CPC sketch. | |
| datasketch_cpc_is_empty() | Return a boolean indicating if the sketch is empty | |
| datasketch_cpc_lower_bound() | Return the lower bound of the number of distinct items seen by the sketch | |
| datasketch_cpc_union() |
Merge a column of sketch_cpc BLOBs into one rollup sketch โ the standard per-partition / per-day rollup pattern.
|
|
| datasketch_cpc_upper_bound() | Return the upper bound of the number of distinct items seen by the sketch | |
|
Frequent Items
Heavy-hitter sketch that identifies the most-frequent items in a stream along with confidence-bounded frequency estimates. Use for top-N analysis on high-cardinality streams where exact |
||
| datasketch_frequent_items() | Aggregate input values into a Frequent Items (heavy-hitter) sketch. | |
| datasketch_frequent_items_epsilon() | Returns the epsilon value (relative error) of the sketch | |
| datasketch_frequent_items_estimate() | Estimated frequency for a specific item. | |
| datasketch_frequent_items_get_frequent() | Return the heavy-hitter candidates with per-item estimate, lower bound, and upper bound. | |
| datasketch_frequent_items_is_empty() | Returns true if the sketch is empty | |
| datasketch_frequent_items_lower_bound() | Returns the lower bound frequency estimate for a specific item | |
| datasketch_frequent_items_num_active() | Returns the number of active items currently tracked by the sketch | |
| datasketch_frequent_items_total_weight() | Returns the total weight (sum of all item counts) processed by the sketch | |
| datasketch_frequent_items_upper_bound() | Returns the upper bound frequency estimate for a specific item | |
|
HLL
HyperLogLog distinct-counting sketch โ the industry standard. Fast serialize/deserialize and broad cross-system compatibility. Choose this when speed and interop matter more than storage. |
||
| datasketch_hll() | Aggregate input values into a HyperLogLog sketch for distinct counting. | |
| datasketch_hll_describe() | Return a string representation of the sketch | |
| datasketch_hll_estimate() | Read the estimated distinct count from an HLL sketch. | |
| datasketch_hll_is_compact() | Return whether the sketch is in compact form | |
| datasketch_hll_is_empty() | Return a boolean indicating if the sketch is empty | |
| datasketch_hll_lg_config_k() | Return the value of log base 2 K for this sketch | |
| datasketch_hll_lower_bound() | Lower bound of the HLL distinct-count estimate at a given number of standard deviations. | |
| datasketch_hll_union() | Merge multiple HLL sketches into one. | |
| datasketch_hll_upper_bound() | Upper bound of the HLL distinct-count estimate at a given number of standard deviations. | |
|
KLL
KLL quantile sketch โ modern mergeable quantile estimator. Best balance of accuracy, speed, and size for general-purpose quantile work. Default choice unless you have a specific reason to pick TDigest or REQ. |
||
| datasketch_kll() | Aggregate input values into a KLL quantile sketch. | |
| datasketch_kll_cdf() | CDF over a list of split points โ one call returns the cumulative rank at each. | |
| datasketch_kll_describe() | Return a description of this sketch | |
| datasketch_kll_is_empty() | Return a boolean indicating if the sketch is empty | |
| datasketch_kll_is_estimation_mode() | Return a boolean indicating if the sketch is in estimation mode | |
| datasketch_kll_k() | Return the value of K for this sketch | |
| datasketch_kll_max_item() | Return the maxium item in the sketch | |
| datasketch_kll_min_item() | Return the minimum item in the sketch | |
| datasketch_kll_n() | Return the number of items contained in the sketch | |
| datasketch_kll_normalized_rank_error() | Return the normalized rank error of the sketch | |
| datasketch_kll_num_retained() | Return the number of retained items in the sketch | |
| datasketch_kll_pmf() | PMF (probability mass) over a list of split points โ fraction of the distribution falling in each bucket. | |
| datasketch_kll_quantile() |
Approximate quantile at a given rank โ given a sketch and r โ [0, 1], returns the value at that rank in the sorted distribution.
|
|
| datasketch_kll_rank() |
Inverse of datasketch_kll_quantile โ given a value, return its approximate rank r โ [0, 1] in the sorted distribution.
|
|
|
Quantiles
Classic mergeable quantile sketch from the original DataSketches paper. Solid general-purpose choice supporting all numeric types; KLL is now preferred for new projects. |
||
| datasketch_quantiles() | Aggregate input values into the classic mergeable quantiles sketch from the original DataSketches paper. | |
| datasketch_quantiles_cdf() | Return the Cumulative Distribution Function (CDF) of the sketch for a series of points | |
| datasketch_quantiles_describe() | Return a description of this sketch | |
| datasketch_quantiles_is_empty() | Return a boolean indicating if the sketch is empty | |
| datasketch_quantiles_is_estimation_mode() | Return a boolean indicating if the sketch is in estimation mode | |
| datasketch_quantiles_k() | Return the value of K for this sketch | |
| datasketch_quantiles_max_item() | Return the maxium item in the sketch | |
| datasketch_quantiles_min_item() | Return the minimum item in the sketch | |
| datasketch_quantiles_n() | Return the number of items contained in the sketch | |
| datasketch_quantiles_normalized_rank_error() | Return the normalized rank error of the sketch | |
| datasketch_quantiles_num_retained() | Return the number of retained items in the sketch | |
| datasketch_quantiles_pmf() | Return the Probability Mass Function (PMF) of the sketch for a series of points | |
| datasketch_quantiles_quantile() | Approximate quantile at a given rank from a classic Quantiles sketch. | |
| datasketch_quantiles_rank() | Approximate rank of a value within the classic Quantiles sketch. | |
|
REQ
Relative-error quantile sketch. Error scales with rank rather than being fixed across the distribution โ predictable accuracy on highly skewed data. |
||
| datasketch_req() | Aggregate input values into a Relative Error Quantile sketch. | |
| datasketch_req_cdf() | Return the Cumulative Distribution Function (CDF) of the sketch for a series of points | |
| datasketch_req_describe() | Return a description of this sketch | |
| datasketch_req_is_empty() | Return a boolean indicating if the sketch is empty | |
| datasketch_req_is_estimation_mode() | Return a boolean indicating if the sketch is in estimation mode | |
| datasketch_req_k() | Return the value of K for this sketch | |
| datasketch_req_max_item() | Return the maxium item in the sketch | |
| datasketch_req_min_item() | Return the minimum item in the sketch | |
| datasketch_req_n() | Return the number of items contained in the sketch | |
| datasketch_req_num_retained() | Return the number of retained items in the sketch | |
| datasketch_req_pmf() | Return the Probability Mass Function (PMF) of the sketch for a series of points | |
| datasketch_req_quantile() | Approximate quantile at a given rank from a REQ sketch. | |
| datasketch_req_rank() | Approximate rank of a value within the REQ sketch. | |
|
TDigest
t-digest โ quantile sketch optimized for the tails (p99, p999). Most accurate where SLOs live; FLOAT/DOUBLE input only. Use when tail latencies or outliers matter more than median accuracy. |
||
| datasketch_tdigest() | Aggregate input values into a t-digest quantile sketch โ most accurate at the tails (p99, p999), exactly where SLOs live. | |
| datasketch_tdigest_cdf() | Return the Cumulative Distribution Function (CDF) of the sketch for a series of points | |
| datasketch_tdigest_describe() | Return a description of this sketch | |
| datasketch_tdigest_is_empty() | Return a boolean indicating if the sketch is empty | |
| datasketch_tdigest_k() | Return the value of K for this sketch | |
| datasketch_tdigest_pmf() | Return the Probability Mass Function (PMF) of the sketch for a series of points | |
| datasketch_tdigest_quantile() | Approximate quantile at a given rank from a t-digest sketch. | |
| datasketch_tdigest_rank() | Approximate rank of a value within the t-digest sketch. | |
| datasketch_tdigest_total_weight() | Return the total weight of this sketch | |
|
Theta
Theta sketch โ the only distinct-count family that supports set operations beyond union. Combine cohorts with |
||
| datasketch_theta() | Aggregate input values into a Theta sketch โ the distinct-count family that supports set operations (union, intersect, A-not-B) beyond simple merge. | |
| datasketch_theta_a_not_b() |
Approximate |A \ B| โ distinct items present in A but not in B.
|
|
| datasketch_theta_describe() | Returns a human-readable description of the Theta sketch | |
| datasketch_theta_estimate() | Read the estimated distinct count from a Theta sketch. | |
| datasketch_theta_get_seed() | Returns the seed hash used by the sketch | |
| datasketch_theta_get_theta() | Returns the theta value of the sketch (sampling probability) | |
| datasketch_theta_intersect() |
Approximate |A โฉ B| โ distinct items present in both cohorts.
|
|
| datasketch_theta_is_empty() | Returns true if the Theta sketch is empty | |
| datasketch_theta_is_estimation_mode() | Returns true if the sketch is in estimation mode (has exceeded exact counting capacity) | |
| datasketch_theta_lower_bound() | Returns the lower bound estimate at the given number of standard deviations (1, 2, or 3) | |
| datasketch_theta_num_retained() | Returns the number of hash values retained in the sketch | |
| datasketch_theta_union() |
Approximate |A โช B| โ distinct count across the union of two cohorts.
|
|
| datasketch_theta_upper_bound() | Returns the upper bound estimate at the given number of standard deviations (1, 2, or 3) | |
|
Types
|
||
| sketch_cpc | Logical type registered by this extension. | |
| sketch_frequent_items | Logical type registered by this extension. | |
| sketch_hll | Logical type registered by this extension. | |
| sketch_kll_bigint | Logical type registered by this extension. | |
| sketch_kll_double | Logical type registered by this extension. | |
| sketch_kll_float | Logical type registered by this extension. | |
| sketch_kll_integer | Logical type registered by this extension. | |
| sketch_kll_smallint | Logical type registered by this extension. | |
| sketch_kll_tinyint | Logical type registered by this extension. | |
| sketch_kll_ubigint | Logical type registered by this extension. | |
| sketch_kll_uinteger | Logical type registered by this extension. | |
| sketch_kll_usmallint | Logical type registered by this extension. | |
| sketch_kll_utinyint | Logical type registered by this extension. | |
| sketch_quantiles_bigint | Logical type registered by this extension. | |
| sketch_quantiles_double | Logical type registered by this extension. | |
| sketch_quantiles_float | Logical type registered by this extension. | |
| sketch_quantiles_integer | Logical type registered by this extension. | |
| sketch_quantiles_smallint | Logical type registered by this extension. | |
| sketch_quantiles_tinyint | Logical type registered by this extension. | |
| sketch_quantiles_ubigint | Logical type registered by this extension. | |
| sketch_quantiles_uinteger | Logical type registered by this extension. | |
| sketch_quantiles_usmallint | Logical type registered by this extension. | |
| sketch_quantiles_utinyint | Logical type registered by this extension. | |
| sketch_req_bigint | Logical type registered by this extension. | |
| sketch_req_double | Logical type registered by this extension. | |
| sketch_req_float | Logical type registered by this extension. | |
| sketch_req_integer | Logical type registered by this extension. | |
| sketch_req_smallint | Logical type registered by this extension. | |
| sketch_req_tinyint | Logical type registered by this extension. | |
| sketch_req_ubigint | Logical type registered by this extension. | |
| sketch_req_uinteger | Logical type registered by this extension. | |
| sketch_req_usmallint | Logical type registered by this extension. | |
| sketch_req_utinyint | Logical type registered by this extension. | |
| sketch_tdigest_double | Logical type registered by this extension. | |
| sketch_tdigest_float | Logical type registered by this extension. | |
| sketch_theta | Logical type registered by this extension. | |
No extension contents match that search.
API Reference
Function Documentation
Data model
Registered Types
Logical types this extension adds to DuckDB. Functions in the reference may accept or return these names directly.
-
sketch_cpcLogical type registered by this extension.
-
sketch_frequent_itemsLogical type registered by this extension.
-
sketch_hllLogical type registered by this extension.
-
sketch_kll_bigintLogical type registered by this extension.
-
sketch_kll_doubleLogical type registered by this extension.
-
sketch_kll_floatLogical type registered by this extension.
-
sketch_kll_integerLogical type registered by this extension.
-
sketch_kll_smallintLogical type registered by this extension.
-
sketch_kll_tinyintLogical type registered by this extension.
-
sketch_kll_ubigintLogical type registered by this extension.
-
sketch_kll_uintegerLogical type registered by this extension.
-
sketch_kll_usmallintLogical type registered by this extension.
-
sketch_kll_utinyintLogical type registered by this extension.
-
sketch_quantiles_bigintLogical type registered by this extension.
-
sketch_quantiles_doubleLogical type registered by this extension.
-
sketch_quantiles_floatLogical type registered by this extension.
-
sketch_quantiles_integerLogical type registered by this extension.
-
sketch_quantiles_smallintLogical type registered by this extension.
-
sketch_quantiles_tinyintLogical type registered by this extension.
-
sketch_quantiles_ubigintLogical type registered by this extension.
-
sketch_quantiles_uintegerLogical type registered by this extension.
-
sketch_quantiles_usmallintLogical type registered by this extension.
-
sketch_quantiles_utinyintLogical type registered by this extension.
-
sketch_req_bigintLogical type registered by this extension.
-
sketch_req_doubleLogical type registered by this extension.
-
sketch_req_floatLogical type registered by this extension.
-
sketch_req_integerLogical type registered by this extension.
-
sketch_req_smallintLogical type registered by this extension.
-
sketch_req_tinyintLogical type registered by this extension.
-
sketch_req_ubigintLogical type registered by this extension.
-
sketch_req_uintegerLogical type registered by this extension.
-
sketch_req_usmallintLogical type registered by this extension.
-
sketch_req_utinyintLogical type registered by this extension.
-
sketch_tdigest_doubleLogical type registered by this extension.
-
sketch_tdigest_floatLogical type registered by this extension.
-
sketch_thetaLogical type registered by this extension.
Practical Examples
Cookbook
Real-world recipes and patterns for common use cases.
Recipes for distinct counts, quantiles, and heavy hitters with Apache DataSketches.
Distinct count with HLL
The bread-and-butter sketch โ approximate COUNT(DISTINCT) in a few KB of memory.
SELECT datasketch_hll_estimate(datasketch_hll(12, user_id)) AS distinct_usersFROM events;The 12 is lg_k โ base-2 log of the bucket count. lg_k = 12 (4096 buckets, ~4 KB sketch, ~1.6% standard error) is a common default. Bracket the estimate with datasketch_hll_lower_bound / datasketch_hll_upper_bound when you want a confidence interval.
Per-day HLL sketches, rolled up on demand
Build the sketch state once; query any window without rescanning.
CREATE TABLE daily_uniques (day DATE, hll sketch_hll);
INSERT INTO daily_uniquesSELECT date_trunc('day', ts) AS day, datasketch_hll(12, user_id) AS hllFROM eventsGROUP BY 1;
-- Rolling 7-day uniques โ reads sketch state, not source eventsSELECT datasketch_hll_estimate(datasketch_hll_union(12, hll)) AS uniques_last_7dFROM daily_uniquesWHERE day >= CURRENT_DATE - 7;The same pattern works for datasketch_cpc (smaller on disk, slower to write) and datasketch_theta (supports set operations beyond union).
Set operations with Theta โ funnel, retention, churn
Theta is the only distinct-count family with full set algebra.
WITH a AS (SELECT datasketch_theta(12, user_id) AS s FROM events_jan), b AS (SELECT datasketch_theta(12, user_id) AS s FROM events_feb)SELECT datasketch_theta_estimate(datasketch_theta_union(a.s, b.s)) AS jan_or_feb, datasketch_theta_estimate(datasketch_theta_intersect(a.s, b.s)) AS retained, -- in both datasketch_theta_estimate(datasketch_theta_a_not_b(a.s, b.s)) AS churned, -- jan not feb datasketch_theta_estimate(datasketch_theta_a_not_b(b.s, a.s)) AS new_users -- feb not janFROM a, b;This is the headline use case for Theta โ funnel and cohort analysis directly off sketch state.
p50 / p95 / p99 with KLL
KLL is the recommended general-purpose quantile sketch.
WITH agg AS ( SELECT datasketch_kll(200, latency_ms) AS sketch FROM requests)SELECT datasketch_kll_quantile(sketch, 0.50, true) AS p50, datasketch_kll_quantile(sketch, 0.95, true) AS p95, datasketch_kll_quantile(sketch, 0.99, true) AS p99FROM agg;K = 200 is a sensible production starting point. Higher K โ smaller error, larger sketch. The trailing true is the inclusive flag โ true for inclusive search (P[X โค q]), false for exclusive (P[X < q]). Use datasketch_kll_normalized_rank_error to inspect the actual rank-error guarantee for the configured K.
Histograms in one query โ KLL CDF / PMF
Get the cumulative distribution at any set of split points without a second pass.
SELECT datasketch_kll_cdf( datasketch_kll(200, latency_ms), [10, 50, 100, 250, 500, 1000], /* inclusive */ true ) AS cdfFROM requests;Returns one row of cumulative ranks at each split point โ directly usable for histograms. Swap cdf for datasketch_kll_pmf to get per-bucket probability mass instead.
Tail-accurate quantiles with TDigest
When p99 / p999 matters more than the median (SLO reporting), reach for t-digest.
CREATE TABLE readings(temp DOUBLE);INSERT INTO readings(temp) SELECT unnest(generate_series(1, 10))::DOUBLE;
-- Rank of value 5 within the aggregated sketchSELECT datasketch_tdigest_rank(datasketch_tdigest(100, temp), 5) AS rank_of_5FROM readings;Output
| rank_of_5 |
|---|
| 0.45 |
CDF over multiple split points in one call:
SELECT datasketch_tdigest_cdf(datasketch_tdigest(100, temp), [1, 5, 9]) AS cdfFROM readings;Output
| cdf |
|---|
| [0.0357, 0.3214, 0.6071, 1.0] |
TDigest accepts FLOAT/DOUBLE only. For integer or skewed-rank quantile work, see datasketch_kll or datasketch_req.
Top-K heavy hitters with Frequent Items
Find the most frequent items in a stream โ with confidence bounds on each estimate.
SELECT datasketch_frequent_items_get_frequent( datasketch_frequent_items(8, country_code), 'NO_FALSE_POSITIVES' ) AS heavy_hittersFROM page_views;The result is a list of structs โ (item, estimate, lower_bound, upper_bound). Use 'NO_FALSE_POSITIVES' for items that are definitely heavy hitters, 'NO_FALSE_NEGATIVES' for the union of all candidates that might be. The leading 8 is lg_max_map_size โ logโ of the maximum number of tracked items (so 8 โ up to 256 candidates). Bigger values track more candidates and tighten the per-item error bound; typical values run 4โ12.
Skew-aware quantiles with REQ
When the distribution is heavily skewed and you want predictable accuracy across the whole rank space, REQ scales its error with rank rather than fixing it.
SELECT datasketch_req_quantile(datasketch_req(12, value), 0.001, true) AS p001, datasketch_req_quantile(datasketch_req(12, value), 0.5, true) AS p50, datasketch_req_quantile(datasketch_req(12, value), 0.999, true) AS p999FROM measurements;Persist sketches to Parquet, merge in another DuckDB
Sketch BLOBs are portable across processes and across DuckDB instances:
COPY (SELECT day, hll FROM daily_uniques) TO 'daily_uniques.parquet' (FORMAT PARQUET);
-- Later, anywhere with this extension loaded:SELECT datasketch_hll_estimate(datasketch_hll_union(12, hll))FROM read_parquet('daily_uniques.parquet')WHERE day BETWEEN DATE '2026-01-01' AND DATE '2026-01-31';The serialization format is the same one used by Druid, Pinot, BigQuery, and the Apache DataSketches Java library โ sketches built here can be merged in those systems and vice versa.
Platform Support
Compatibility
Extension availability may vary by platform and DuckDB version. Check below to ensure this extension supports your environment before installation.
Quick Facts
Platforms
- Linux x86_64 aarch64
- Linux (musl) Not available
- macOS Intel Apple Silicon
- Windows x86_64
- WASM eh mvp threads
Compiled binary sizes
| Platform | Architecture | Size |
|---|---|---|
| Linux | x86_64 | 4.46 MB |
| Linux | aarch64 | 3.93 MB |
| macOS | Intel | 3.26 MB |
| macOS | Apple Silicon | 2.88 MB |
| Windows | x86_64 | 8.21 MB |
| WASM | eh | 612.6 KB |
| WASM | mvp | 556.0 KB |
| WASM | threads | 611.7 KB |
Compressed download size from the Haybarn extension repository.
DuckDB & Haybarn
Release calendar- DuckDB v1.5.5 Haybarn 1.5.5-rc1 Supported