Раздел 42 · Аналитика данных с нуля

Что такое окно: OVER()

middle~15 мин

без входаВойди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти

Переговорка «Эпохи Корон» похожа на штаб перед битвой: на столе карта Трёх Корон и пластиковые фигурки шести фракций, маг с посохом стоит в центре. На экране письмо Кима: «маги на 1.4 выигрывают 53,3%, нерфить».

Блок E закончился боссом про опоздания «Гонца»: Отто признал, что дашборд врал, и агентство взяло курс на тендер «Монетки». Для тендера нужны два готовых кейса, и Ильва может стать вторым, если ответ окажется честным, а не удобным. Она отвечает за баланс онлайн-стратегии «Эпоха Корон»: шесть фракций, патчи каждые полтора месяца и вечный спор игроков, кто сильнее. База empires хранит полгода матчей: matches описывает сам матч (карта, режим, ранговый или нет, патч, длительность), а match_players хранит по строке на каждого участника: фракцию, команду, результат, APM, рейтинг до и после.

Цифру Кима мы пока не трогаем: проверим её в боссе блока, когда соберём все инструменты. Весь блок про один инструмент, оконные функции. Они закрывают задачи, которые без них требуют громоздких подзапросов: доли, рейтинги, нарастающие итоги, серии. Сегодня разберёмся, чем окно отличается от GROUP BY.

Таблица в классном журнале

Учитель ставит оценки за контрольную. Можно составить сводку «средний балл по классу 3,8». Это GROUP BY: тридцать строк превратились в одну, и кто что получил, уже не видно.

А можно оставить журнал как есть и дописать справа столбец «средний по классу». В каждой строке тот же 3,8, но рядом с оценкой каждого ученика. Теперь видно, кто выше среднего, а кто ниже, и ни одна строка не пропала.

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

Сначала по-старому

Средний APM по фракциям Ильва уже видела. Это обычная группировка из урока про GROUP BY:

SELECT f.name AS faction, round(avg(mp.apm), 1) AS avg_apm
FROM match_players mp
JOIN factions f ON f.id = mp.faction_id
GROUP BY f.name
ORDER BY avg_apm DESC;
 faction           | avg_apm
-------------------+---------
 Лесной Союз       |   101.3
 Северные Кланы    |    99.8
 Пустынный Халифат |    98.2
 Империя           |    98.2
 Орден Магов       |    97.6
 Нежить            |    97.6

Шесть строк вместо 31 752. Игроки исчезли, а Ильве нужны они.

OVER(): итог в каждой строке

Возьмём ту же avg и допишем после неё OVER (). Пустые скобки значат «окно это все строки результата»:

SELECT match_id, player_id, apm,
       count(*)              OVER () AS all_rows,
       round(avg(apm) OVER (), 1) AS avg_apm
FROM match_players
ORDER BY match_id, player_id
LIMIT 4;
 match_id | player_id | apm | all_rows | avg_apm
----------+-----------+-----+----------+---------
        1 |       679 | 121 |    31752 |    98.8
        1 |       833 | 119 |    31752 |    98.8
        1 |      1648 |  68 |    31752 |    98.8
        1 |      2117 | 105 |    31752 |    98.8

Строки на месте, а справа в каждой стоит итог по всей таблице: всего строк 31 752, средний APM 98,8. GROUP BY здесь нет, и он не нужен.

урок из курса

Урок из курса «Аналитика данных с нуля». Курс покупается один раз и остаётся навсегда: 500 ₽.

Оплата пока не подключена: премиум и курсы открываются промокодом.