WIDTH_BUCKET — равные бакеты и гистограммы распределения в PostgreSQL

WIDTH_BUCKET(operand, low, high, count) распределяет значение по одному из count равных по ширине интервалов на отрезке [low, high) — готовый инструмент для гистограмм и бакетирования. Аналитику это упрощает распределения сумм чеков, возраста, времени отклика без ручной арифметики. Функция есть в PostgreSQL и Oracle, но отсутствует в MySQL и SQLite. Значения вне диапазона попадают в служебные корзины: меньше low — в 0, не меньше high — в count+1.

Синтаксис

WIDTH_BUCKET(operand, low, high, count)

Примеры использования

Пример 1: Гистограмма сумм заказов

SELECT
  WIDTH_BUCKET(amount, 0, 5000, 5) AS bucket,
  COUNT(*) AS orders
FROM orders
GROUP BY bucket
ORDER BY bucket;

Результат:

bucket | orders
-------+-------
     1 |    412
     2 |    233
     6 |      7

Пример 2: Возрастные группы пользователей

SELECT
  WIDTH_BUCKET(age, 18, 68, 5) AS age_group,
  COUNT(*) AS users
FROM users
GROUP BY age_group
ORDER BY age_group;

Результат:

age_group | users
----------+------
        1 |   140
        2 |   201
        3 |    98

Пример 3: Границы: корзины 0 и count+1

SELECT
  WIDTH_BUCKET(-50, 0, 100, 4) AS below,
  WIDTH_BUCKET(150, 0, 100, 4) AS above;

Результат:

below | above
------+------
    0 |     5
Порешать SQL-задачи в тренажёре →

Типичная ошибка

Бакеты полуоткрытые [low, high): значение, равное low, попадает в корзину 1, а значение, равное high, — уже в переполнение. Значения ниже low дают 0, значения ≥ high дают count+1 (итого count+2 корзины). Функция есть в PostgreSQL и Oracle, но НЕТ в MySQL и SQLite — там считай FLOOR((x - low) / step) + 1 или CASE вручную. В PostgreSQL есть и вторая форма WIDTH_BUCKET(operand, ARRAY[...]) с произвольными границами.

Где спрашивают на собеседовании

Спрашивают при построении гистограмм и распределений (суммы чеков, возраст, latency) в паре с GROUP BY. Частый follow-up — как воспроизвести бакетирование без WIDTH_BUCKET в MySQL или SQLite.

FAQ: частые вопросы про WIDTH_BUCKET

Есть ли WIDTH_BUCKET в MySQL или SQLite?

Нет, WIDTH_BUCKET поддерживают PostgreSQL и Oracle, а в MySQL и SQLite её нет. Там равные бакеты считают арифметикой: FLOOR((x - low) / step) + 1, где step = (high - low) / count, либо через набор CASE WHEN с явными границами. Значения вне диапазона нужно обрабатывать самому.

Почему появился бакет 0 или count+1?

Это служебные корзины для значений вне заданного диапазона. Всё, что меньше low, попадает в 0, а всё, что больше или равно high, — в count+1. Так WIDTH_BUCKET не теряет выбросы; если они не нужны, отфильтруй их в WHERE или объедини крайние корзины.

Как задать неравные интервалы?

Для произвольных границ в PostgreSQL используй форму с массивом: WIDTH_BUCKET(operand, ARRAY[100, 500, 1000, 5000]). Массив должен быть отсортирован по возрастанию, а функция вернёт номер интервала, в который попал operand. Это удобно для бизнес-порогов, которые не равны по ширине.

Задачи в тренажёре по теме

Вопросы с собеседований по теме

Смежные темы SQL

Агрегации · Условная логика

Другие функции SQL

COALESCE · NULLIF · CASE WHEN · STRING_AGG · COUNT · SUM OVER

Открыть SQL-тренажёр (565 задач) →