Медиана в PostgreSQL: как вычислить и использовать в запросах

Пройдите тест, узнайте какой профессии подходите

Я предпочитаю
0%
Работать самостоятельно и не зависеть от других
Работать в команде и рассчитывать на помощь коллег
Организовывать и контролировать процесс работы

Для кого эта статья:

  • Специалисты по анализу данных и аналитики, работающие с PostgreSQL
  • Студенты и профессионалы, обучающиеся навыкам работы с SQL и анализу данных
  • Разработчики, заинтересованные в оптимизации запросов при работе с большими наборами данных

Владея сотнями инструментов анализа данных, аналитики часто упускают возможности правильного использования медианы в PostgreSQL. Расчёт этой метрики вызывает затруднения у 68% специалистов, работающих с базами данных. Причина проста — отсутствие встроенной функции для её вычисления. Однако знание нескольких элегантных решений позволяет превратить сложную задачу в рутинную операцию. Давайте разберём, как мастерски использовать медиану для получения точных аналитических выводов, избегая распространённых ошибок и оптимизируя запросы для работы даже с гигантскими наборами данных. 🔍

Хотите систематизировать знания по анализу данных с использованием SQL? Курс «SQL для анализа данных» от Skypro поможет освоить не только расчёт медианы, но и другие статистические функции в PostgreSQL. Курс построен на практических задачах из реальных проектов — вы научитесь писать оптимизированные запросы, которые не "падают" под нагрузкой. Более 82% выпускников отмечают рост производительности своих запросов в 3-5 раз после применения полученных техник.

Что такое медиана и почему она важна в анализе данных

Медиана — это значение, которое делит упорядоченный набор данных ровно пополам. В отличие от среднего арифметического, медиана нечувствительна к выбросам и аномалиям, что делает её незаменимым инструментом для анализа несимметричных распределений.

Рассмотрим простой пример. В компании работает 9 человек с такими зарплатами (в тысячах рублей): 70, 75, 80, 85, 90, 95, 100, 300, 400.

  • Среднее арифметическое: 143 тыс. руб.
  • Медиана: 90 тыс. руб.

Видите разницу? Среднее значительно искажено двумя высокими зарплатами, тогда как медиана даёт более реалистичную картину того, что получает "типичный" сотрудник. 📊

Медиана особенно полезна в следующих сценариях:

  • Анализ доходов и других экономических показателей
  • Оценка времени отклика систем (когда отдельные длительные операции могут исказить среднее)
  • Анализ рейтингов и оценок пользователей
  • Обработка данных с выраженной асимметрией распределения

В статистической аналитике медиана является одной из мер центральной тенденции наряду со средним и модой. Она относится к семейству процентилей — 50-й процентиль и есть медиана.

МетрикаУстойчивость к выбросамПрименение
СреднееНизкаяСимметричные распределения без выбросов
МедианаВысокаяАсимметричные распределения, наличие выбросов
МодаВысокаяКатегориальные данные, дискретные распределения

К сожалению, несмотря на всю свою пользу, медиана не имеет встроенной функции в PostgreSQL, в отличие от avg() для среднего. Но это не означает, что вы не можете её эффективно использовать. Давайте рассмотрим, как это сделать.

Кинга Идем в IT: пошаговый план для смены профессии

Базовые методы вычисления медианы в PostgreSQL

В PostgreSQL существует несколько подходов к расчёту медианы. Начнём с самых базовых, которые подходят для небольших наборов данных.

Метод 1: Использование процентиля

Наиболее прямолинейный способ — использование функции percentile_cont() или percentile_disc():

SELECT 
percentile_cont(0.5) WITHIN GROUP (ORDER BY column_name) AS median
FROM table_name;

Здесь:

  • percentile_cont() — вычисляет непрерывный процентиль, который может интерполировать значение между двумя записями
  • 0.5 — соответствует 50-му процентилю, то есть медиане
  • WITHIN GROUP (ORDER BY column_name) — указывает столбец и порядок сортировки

Аналогично можно использовать percentile_disc(), который всегда возвращает фактическое значение из набора данных:

SELECT 
percentile_disc(0.5) WITHIN GROUP (ORDER BY column_name) AS median
FROM table_name;

Алексей Петров, Lead Data Analyst

Когда я только начинал работать с PostgreSQL, мы анализировали время доставки заказов в крупной логистической компании. Руководство жаловалось, что среднее время доставки — 6 дней — не соответствует реальности. Посмотрев на данные, я заметил проблему: редкие случаи очень долгих доставок (20-30 дней) сильно искажали картину.

Вместо среднего я применил медиану:

SELECT 
avg(delivery_days) as avg_time,
percentile_cont(0.5) WITHIN GROUP (ORDER BY delivery_days) as median_time
FROM deliveries;

Результат показал, что медианное время доставки составляло всего 3 дня! Эта метрика гораздо лучше отражала типичный опыт клиентов. Мы перестроили KPI отдела логистики на основе медианы и 90-го процентиля. Это позволило фокусироваться не только на средних показателях, но и на улучшении обслуживания проблемных случаев, значительно повысив удовлетворенность клиентов.

Метод 2: Расчёт с помощью ROW_NUMBER()

Альтернативный подход — использование функции ROW_NUMBER() для ранжирования строк:

WITH ranked_data AS (
SELECT
column_name,
ROW_NUMBER() OVER (ORDER BY column_name) as row_num,
COUNT(*) OVER () as total_rows
FROM table_name
)
SELECT AVG(column_name) as median
FROM ranked_data
WHERE row_num BETWEEN total_rows/2.0 AND total_rows/2.0 + 1;

Этот метод работает, вычисляя "среднюю строку" для набора данных. Для чётного числа строк он возьмёт среднее между двумя центральными значениями.

Преимущества и недостатки различных методов расчёта медианы:

МетодПреимуществаНедостаткиРекомендуется для
percentile_cont()Более читаемый код, поддержка непрерывных значенийМожет быть менее производительным на очень больших наборах данныхБольшинства аналитических задач
percentile_disc()Всегда возвращает реальное значение из набораМожет давать менее точное представление о центральной тенденцииКогда нужно фактическое значение из набора
ROW_NUMBER()Более гибкий подход с возможностью модификацииБолее многословный кодСложных случаев с дополнительной логикой

Для небольших и средних наборов данных любой из этих методов будет работать эффективно. Однако при работе с большими таблицами требуется оптимизация. 🚀

Оптимизация запросов для расчета медианы больших таблиц

Работа с большими объёмами данных требует особого внимания к оптимизации запросов для расчета медианы. Наивные подходы могут привести к исключительно медленному выполнению или даже сбоям из-за нехватки памяти.

Вот несколько эффективных стратегий для оптимизации:

1. Использование индексов

Создание индекса по столбцу, для которого вычисляется медиана, может значительно ускорить процесс:

CREATE INDEX idx_column_name ON table_name(column_name);

Индекс поможет при наличии ORDER BY в запросе, что особенно актуально при расчёте медианы.

2. Предварительная фильтрация

Если вам нужна медиана только для определённого подмножества данных, фильтруйте как можно раньше:

SELECT 
percentile_cont(0.5) WITHIN GROUP (ORDER BY value) AS median
FROM large_table
WHERE created_at >= '2025-01-01'
AND category = 'electronics';

Такой подход уменьшает объем обрабатываемых данных, что существенно ускоряет выполнение.

3. Использование приближенных методов

Для очень больших наборов данных можно использовать приближенные методы расчета медианы:

-- Приближенный расчет медианы на основе выборки
WITH sample_data AS (
SELECT value
FROM large_table
TABLESAMPLE BERNOULLI(10) -- 10% выборка
)
SELECT 
percentile_cont(0.5) WITHIN GROUP (ORDER BY value) AS approximate_median
FROM sample_data;

Такой подход работает значительно быстрее, но даёт приблизительный результат. Для многих аналитических задач такой точности достаточно.

4. Материализованные представления

Если медиана вычисляется регулярно, можно использовать материализованные представления:

CREATE MATERIALIZED VIEW median_stats AS
SELECT 
category,
percentile_cont(0.5) WITHIN GROUP (ORDER BY value) AS median
FROM large_table
GROUP BY category;

-- Обновление представления
REFRESH MATERIALIZED VIEW median_stats;

Медиана будет предварительно рассчитана и сохранена, что делает последующие запросы мгновенными.

5. Партиционирование таблиц

Для особо больших таблиц (миллиарды строк) эффективно работает партиционирование:

CREATE TABLE measurements (
id serial,
value numeric,
created_at timestamp
) PARTITION BY RANGE (created_at);

-- Создание партиций
CREATE TABLE measurements_2025_q1 PARTITION OF measurements
FOR VALUES FROM ('2025-01-01') TO ('2025-04-01');

CREATE TABLE measurements_2025_q2 PARTITION OF measurements
FOR VALUES FROM ('2025-04-01') TO ('2025-07-01');

-- Запрос медианы для конкретного периода будет использовать только нужную партицию
SELECT 
percentile_cont(0.5) WITHIN GROUP (ORDER BY value) AS median
FROM measurements
WHERE created_at BETWEEN '2025-01-01' AND '2025-03-31';

Сравнительная эффективность различных методов оптимизации:

Метод оптимизацииУскорение (приблизительно)Сложность реализацииПодходит для таблиц
Индексирование2-10xНизкаяЛюбого размера
Предварительная фильтрацияЗависит от фильтраНизкаяЛюбого размера
Приближенные методы10-100xСредняя> 1 млн строк
Материализованные представления10-1000x для чтенияСредняя> 100 тыс. строк
Партиционирование5-50xВысокая> 10 млн строк

Правильный выбор метода оптимизации зависит от конкретной задачи, размера данных и требований к производительности. Для критичных систем рекомендуется комбинировать несколько подходов. 🛠️

Использование оконных функций для медианы в PostgreSQL

Оконные функции (window functions) — один из самых мощных инструментов PostgreSQL для продвинутой аналитики. С их помощью можно вычислять медиану по группам или скользящим окнам, что открывает широкие возможности для анализа.

Медиана по группам

Расчёт медианы для разных групп данных:

SELECT 
category,
percentile_cont(0.5) WITHIN GROUP (ORDER BY price) AS median_price
FROM products
GROUP BY category;

Этот запрос вернёт медианную цену для каждой категории товаров. Но настоящая сила оконных функций проявляется, когда нам нужно сравнить индивидуальное значение с медианой группы:

SELECT 
product_id,
name,
price,
category,
percentile_cont(0.5) WITHIN GROUP (ORDER BY price) 
OVER (PARTITION BY category) AS category_median_price,
price – (percentile_cont(0.5) WITHIN GROUP (ORDER BY price) 
OVER (PARTITION BY category)) AS diff_from_median
FROM products
ORDER BY category, diff_from_median DESC;

Этот запрос покажет, насколько цена каждого продукта отклоняется от медианы в своей категории.

Скользящая медиана

Для анализа временных рядов особенно полезна скользящая (rolling) медиана:

SELECT 
transaction_date,
transaction_amount,
percentile_cont(0.5) WITHIN GROUP (ORDER BY transaction_amount) OVER (
ORDER BY transaction_date 
ROWS BETWEEN 7 PRECEDING AND CURRENT ROW
) AS rolling_7day_median
FROM transactions
ORDER BY transaction_date;

Этот запрос вычисляет 7-дневную скользящую медиану для суммы транзакций, что помогает выявить тренды и сгладить случайные колебания. 📈

Кумулятивная медиана

Иногда полезно видеть, как медиана изменялась со временем по мере накопления данных:

SELECT 
transaction_date,
percentile_cont(0.5) WITHIN GROUP (ORDER BY transaction_amount) OVER (
ORDER BY transaction_date 
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS cumulative_median
FROM transactions
ORDER BY transaction_date;

Этот запрос показывает, как медианное значение суммы транзакций менялось с течением времени, учитывая все данные с начала до текущей даты.

Комбинирование с другими агрегатами

Оконные функции позволяют легко комбинировать медиану с другими статистиками:

SELECT 
category,
product_id,
price,
percentile_cont(0.5) WITHIN GROUP (ORDER BY price) OVER w AS median_price,
avg(price) OVER w AS avg_price,
stddev(price) OVER w AS stddev_price,
price / (percentile_cont(0.5) WITHIN GROUP (ORDER BY price) OVER w) AS price_to_median_ratio
FROM products
WINDOW w AS (PARTITION BY category)
ORDER BY category, price_to_median_ratio DESC;

Такой подход позволяет увидеть полную статистическую картину и оценить, насколько конкретное значение отклоняется от нормы.

Тест на профориентацию от Skypro поможет определить, подходит ли вам карьера аналитика данных. После теста вы получите персональные рекомендации, которые учитывают ваши навыки работы с цифрами и аналитическое мышление. 76% наших студентов, уже работающих с PostgreSQL и медианными вычислениями, изначально не планировали карьеру в аналитике — тест помог им обнаружить скрытый потенциал и выбрать оптимальную образовательную траекторию.

Практические сценарии применения медианы в аналитике

Теоретические знания о медиане в PostgreSQL ценны только тогда, когда применяются для решения практических задач. Рассмотрим несколько реальных сценариев, где медиана доказывает свою незаменимость.

Анализ финансовых показателей

Финансовые данные обычно характеризуются несимметричным распределением, что делает медиану идеальным инструментом для их анализа:

-- Анализ доходности различных инвестиционных инструментов
SELECT 
instrument_type,
count(*) AS num_instruments,
avg(annual_return) AS mean_return,
percentile_cont(0.5) WITHIN GROUP (ORDER BY annual_return) AS median_return,
percentile_cont(0.25) WITHIN GROUP (ORDER BY annual_return) AS q1_return,
percentile_cont(0.75) WITHIN GROUP (ORDER BY annual_return) AS q3_return,
round(stddev(annual_return)::numeric, 2) AS std_return
FROM investments
GROUP BY instrument_type
ORDER BY median_return DESC;

Этот запрос даёт полную статистическую картину по каждому типу инвестиций, где медиана показывает "типичную" доходность, не искажённую экстремальными значениями.

Анализ пользовательского поведения

При анализе поведения пользователей медиана часто выдаёт более точные результаты:

-- Анализ времени, проведённого пользователями в приложении
SELECT 
user_segment,
date_trunc('month', session_date) AS month,
count(DISTINCT user_id) AS active_users,
avg(session_duration_minutes) AS avg_session_time,
percentile_cont(0.5) WITHIN GROUP (ORDER BY session_duration_minutes) AS median_session_time,
-- Сравнение с предыдущим месяцем
percentile_cont(0.5) WITHIN GROUP (ORDER BY session_duration_minutes) – 
lag(percentile_cont(0.5) WITHIN GROUP (ORDER BY session_duration_minutes)) OVER (
PARTITION BY user_segment ORDER BY date_trunc('month', session_date)
) AS median_change_from_prev_month
FROM user_sessions
WHERE session_date >= '2025-01-01'
GROUP BY user_segment, date_trunc('month', session_date)
ORDER BY user_segment, month;

Этот запрос анализирует, как меняется медианное время сессии в приложении для разных сегментов пользователей, что помогает выявить тренды и оценить эффект от внедрения новых функций.

Обнаружение аномалий

Медиана эффективна при поиске аномальных значений и выбросов:

-- Поиск продуктов с аномально высокими ценами относительно категории
WITH price_stats AS (
SELECT
product_id,
name,
category,
price,
percentile_cont(0.5) WITHIN GROUP (ORDER BY price) OVER (PARTITION BY category) AS median_price,
percentile_cont(0.75) WITHIN GROUP (ORDER BY price) OVER (PARTITION BY category) AS q3_price,
percentile_cont(0.25) WITHIN GROUP (ORDER BY price) OVER (PARTITION BY category) AS q1_price
FROM products
)
SELECT
product_id,
name,
category,
price,
median_price,
round((price – median_price) / median_price * 100) AS percent_above_median,
(q3_price – q1_price) * 1.5 AS iqr_range,
price > q3_price + ((q3_price – q1_price) * 1.5) AS is_outlier
FROM price_stats
WHERE price > median_price
ORDER BY percent_above_median DESC;

Этот запрос использует концепцию межквартильного размаха для определения выбросов в данных о ценах продуктов. 🔍

A/B-тестирование

При сравнении экспериментальных групп медиана может дать более надёжные результаты:

-- Сравнение показателей конверсии в разных вариантах эксперимента
SELECT
experiment_variant,
count(*) AS users_count,
avg(conversion_time_seconds) AS avg_conversion_time,
percentile_cont(0.5) WITHIN GROUP (ORDER BY conversion_time_seconds) AS median_conversion_time,
percentile_cont(0.9) WITHIN GROUP (ORDER BY conversion_time_seconds) AS p90_conversion_time
FROM user_experiments
WHERE experiment_id = 'login-page-2025-redesign'
AND conversion = true
GROUP BY experiment_variant;

Этот запрос позволяет сравнить не только средние, но и медианные показатели времени конверсии для разных вариантов дизайна, что даёт более полную картину эффективности каждого варианта.

Ключевые практические рекомендации по использованию медианы:

  • Всегда сравнивайте среднее с медианой — значительное расхождение указывает на асимметрию в распределении
  • Используйте медиану вместе с квартилями (25-й и 75-й процентили) для оценки разброса данных
  • При построении отчётов и дашбордов включайте медиану как один из основных статистических показателей
  • Для временных рядов применяйте скользящую медиану для сглаживания случайных колебаний
  • В отчётах для руководства объясняйте, почему медиана может быть более информативной, чем среднее

Дмитрий Волков, BI-аналитик

В одном из проектов по анализу времени загрузки интернет-магазина мы столкнулись с интересным случаем. Средняя скорость загрузки страниц составляла 4,2 секунды, что выходило за пределы целевых KPI (до 3 секунд). Команда разработчиков готовилась к масштабной оптимизации, но я предложил посмотреть на медиану:

SELECT
page_type,
avg(load_time) AS avg_load_time,
percentile_cont(0.5) WITHIN GROUP (ORDER BY load_time) AS median_load_time,
percentile_cont(0.95) WITHIN GROUP (ORDER BY load_time) AS p95_load_time
FROM page_loads
WHERE date >= current_date – interval '7 days'
GROUP BY page_type;

Результаты показали, что медианное время загрузки было всего 2,1 секунды! Проблема заключалась в периодических длительных задержках у небольшого процента пользователей. Вместо полной переработки мы сосредоточились на выявлении причин этих задержек. Оказалось, что проблема была в определенной рекламной сети, которую использовали только 5% страниц. После оптимизации интеграции с этой сетью 95-й процентиль улучшился с 12 до 4,8 секунд, хотя медиана почти не изменилась. Это решение сэкономило команде месяцы работы и сотни тысяч рублей.

Овладение техниками расчёта и использования медианы в PostgreSQL превращает аналитика из простого исполнителя запросов в настоящего эксперта данных. Медиана часто рассказывает историю, скрытую за средними значениями. Она отражает типичное поведение пользователей, реальные бизнес-показатели и помогает выявить закономерности, спрятанные в асимметричных распределениях. Интегрируя вычисление медианы в ваши регулярные отчёты и аналитические процессы, вы получаете более полную и достоверную картину данных, на основе которой можно принимать точные бизнес-решения.