One region reports p95 latency of 100 ms. Another reports 1,000 ms. Is the overall p95 550 ms? No. A percentile is a position in a distribution, and two summary values do not preserve that distribution.

This example makes the error visible with just 100 requests. Then we will keep mergeable summaries using the DataSketches extension.

Explore the idea

Find the 95th request in the line

Every dot represents one request, sorted by latency. Ninety-five take 100 ms; five take 1,000 ms.

100 ms

the 95th sorted observation

The 95th request is still in the fast group

The cumulative share at 100 ms reaches 95%. The discrete percentile returns that observed value.

Start with a distribution you can count

Ninety-five requests take 100 ms. Five take 1,000 ms. The preview shows one row from each region; the download and setup contain all 100 requests.

requests100 rows

One hundred synthetic requests

IDs 1–95 take 100 ms; IDs 96–100 take 1,000 ms. The preview shows one of each; the CSV includes all 100.

idregionlatency_ms
1fast100
96slow1000

We use the discrete percentile throughout the comparison: sort the observations and select the first observed value whose cumulative proportion reaches the requested fraction. At p95, the 95th observation in this 100-row fixture is still 100 ms.

An interpolated continuous percentile is a different definition. quantile_cont can return a value between observations. Pick a definition before comparing dashboards or testing a sketch against an exact reference.

Regional percentiles lose information

Two regions have very different request counts

SELECT region, count(*) AS requests,
  quantile_disc(latency_ms, 0.95) AS p95_ms
FROM requests GROUP BY region ORDER BY region;
Expected result · sample data2 rows
region requests p95_ms
fast 95 100
slow 5 1000

The fast region contributes 95 requests; the slow region contributes only five. Averaging their p95 values gives each region equal weight, even though their traffic volumes differ dramatically.

Weighting those percentile values by request count seems like a repair. It still does not reconstruct the overall distribution:

Neither average of regional percentiles is the overall p95

WITH regional AS (
 SELECT count(*) AS n, quantile_disc(latency_ms, 0.95) AS p95
 FROM requests GROUP BY region
)
SELECT (SELECT avg(p95) FROM regional) AS mean_of_p95,
  (SELECT sum(n * p95) / sum(n) FROM regional) AS weighted_mean_of_p95,
  quantile_disc(latency_ms, 0.95) AS overall_p95
FROM requests;
Expected result · sample data1 row
mean_of_p95 weighted_mean_of_p95 overall_p95
550 145 100

The simple average is 550 ms, the weighted average is 145 ms, and the exact pooled discrete p95 is 100 ms. The weighted calculation answers a question about regional summary numbers, not the rank of all requests together.

Use DuckDB’s quantile_disc over the pooled observations when you need this exact answer and have the underlying data available.

Merge distributions, then ask the question

A KLL sketch retains a compact approximation of a distribution. Build one per region, merge the sketches, and only then extract the overall percentiles:

Merge the distributions, then ask for percentiles

WITH regional AS (
 SELECT region, datasketch_kll(200, latency_ms) AS sketch
 FROM requests GROUP BY region
), combined AS (
 SELECT datasketch_kll(200, sketch) AS sketch FROM regional
)
SELECT datasketch_kll_quantile(sketch, 0.50, true) AS p50_ms,
  datasketch_kll_quantile(sketch, 0.95, true) AS p95_ms,
  datasketch_kll_quantile(sketch, 0.99, true) AS p99_ms
FROM combined;
Expected result · sample data1 row
p50_ms p95_ms p99_ms
100 100 1000

The first datasketch_kll(200, latency_ms) aggregates numeric observations. The overloaded datasketch_kll(200, sketch) aggregates compatible sketches. The final calls request inclusive quantiles from the combined summary.

For this small fixture, the sketch retains enough information to reproduce p50 = 100, p95 = 100, and p99 = 1,000. That exact agreement is not a promise for larger data. KLL is approximate; its accuracy is described in rank space, which does not translate to a fixed millisecond error when the distribution has a sharp jump.

The parameter 200 controls the sketch’s accuracy/space tradeoff. All component sketches here use the same parameter and numeric input type. See the extension reference before mixing saved states from different configurations.

Your turn · check your answer

Find the overall discrete p99

Return one column, p99_ms, containing the exact discrete 99th percentile across all requests.

See the expected result
Your target result1 row
p99_ms
1000
Sample data is loaded for you.

Preparing the editor…

Need a hint?

Compute the percentile over request observations. An average of regional p99 values answers a different question.

Show a solution
SELECT quantile_disc(latency_ms, 0.99) AS p99_ms FROM requests;

Know what the summary can answer

Merge sketches from disjoint request partitions. Including a region or day twice counts its observations twice; merging does not deduplicate requests. Keep the partition identity beside saved summaries.

A sketch also cannot retroactively answer arbitrary filters whose dimensions you discarded. If you will compare endpoints, preserve summaries at that grain or retain raw observations.

This 100-row exercise demonstrates correctness and the merge pattern, not a performance benchmark. Use real volumes and an explicit error requirement before choosing sketches for a production latency report. The distinct-count lesson explores a related problem with a different kind of mergeable summary.