You can calculate the precise average value of a metric using sumMerge(bucket_sum) / sumMerge(bucket_count). This is often more accurate than estimating an average from bucket midpoints.
Explore documentation
Querying histograms
Histograms are a powerful tool for understanding the distribution of your metric data, such as request latencies or response sizes. Instead of just tracking an average, histograms group observations into configurable buckets, giving you a much richer view of your data's performance and characteristics.
In our system, histogram data is stored in a highly optimized format. This guide will walk you through how this data is structured and how to write SQL queries to visualize it as tables, heatmaps, and even calculate custom quantiles like p95.
For details on how you can send us histograms, see Ingesting metrics API. Histograms and summaries coming from Prometheus and histograms from OpenTelemetry are supported out-of-the-box.
Querying histograms
We store histograms in a unified and efficient way. Histogram data is split into bucket_bounds (the upper limits of the buckets) and bucket_counts (the number of observations in each bucket, stored as deltas). Pre-computed quantiles are available via bucket_quantiles.
Understanding the histogram data structure
| Column | Merge Function | Description |
|---|---|---|
bucket_bounds |
anyLastMerge() |
Histogram bucket boundaries (e.g., [0.1, 0.5, inf]). |
bucket_counts |
sumForEachMerge() |
Counts per bucket (element-wise sum of deltas). |
bucket_quantiles |
histogramQuantile(p) |
Pre-computed quantile function for efficient percentile calculation. |
bucket_sum |
sumMerge() |
Sum of all observed values. |
bucket_count |
sumMerge() |
Total number of observations. |
bucket_min |
minMerge() |
Minimum observed value. |
bucket_max |
maxMerge() |
Maximum observed value. |
Visualizing buckets as a table
To visualize the number of events in each bucket over a selected time range for a metric, you'll join the bucket_bounds with the bucket_counts. Remember that bucket_counts stores deltas, so sumForEachMerge is used to aggregate them.
Fill in the query variable {{metric_name}} with your metric, e.g. http_request_duration_seconds.
Key points for this query:
sumForEachMerge(bucket_counts)correctly aggregates the delta counts across intervals.arrayJoin(arrayZip(bounds, counts))pairs each bound with its corresponding count and explodes the array into separate rows.isFinite(upper_limit)filters out the+Infbucket, which is typically the last element inbucket_boundsand represents the total count.label('status')is an example of including other metric labels for further breakdown.
Creating a heatmap
A heatmap provides a visual representation of how your metric's distribution changes over time. We aggregate bucket_counts to get the total count within each bucket for each time interval.
Key points for this query:
- We
GROUP BY time, series_idfirst to ensure consistent bucket boundaries beforearrayJoin. sumForEachMerge(bucket_counts)is crucial for aggregating the delta counts.isFinite(upper_limit)filters out the+Infbucket.seriesis constructed from theupper_limitto provide meaningful labels for the heatmap.
Calculating custom quantiles (e.g., p95)
Bucket quantiles are stored in the bucket_quantiles aggregate column, which works with the quantilePrometheusHistogramArrayMerge function. A helper function histogramQuantile(p) is available for convenience.
Important: When to use histogramQuantile vs. Conditional Form:
histogramQuantile(p)(helper): Use when yourWHEREclause filters for exactly one metric name. If you query multiple metrics or no specific metric, the histogram buckets from different metrics will be mixed, producing incorrect results.quantilePrometheusHistogramArrayMergeIf(p)(bucket_quantiles, condition): Use when querying multiple metric names or when no specific metric is filtered. Theconditionallows you to apply the quantile calculation selectively.
Example: Single metric quantile calculation (recommended)
Example: Multiple metrics quantile calculation (conditional form)
Histogram Statistics
For basic statistics without manual percentile calculation, you can use the dedicated bucket_sum, bucket_count, bucket_min, and bucket_max aggregate columns.
Need help?
Please let us know at hello@betterstack.com. We're happy to help! 🙏