This is Data
6.28K subscribers
180 photos
202 links
Канал Романа Романчука про аналитику и данные.

Рассказываю про метрики и мат.статистику. Обозреваю ENG и RUS статьи. Советую книги. Делюсь скриптами, ссылками, майндмэпами.

Сайт: https://thisisdata.ru
Задать вопрос: @romanchuk_roman
Download Telegram
Почему оконные функции - это суперсила аналитика?

Знакомы с проблемой? Вам нужно посчитать долю каждой продажи в общей выручке за месяц, сравнить сегодняшний показатель со вчерашним или составить рейтинг товаров внутри категории. Обычные агрегатные функции (с GROUP BY или без него) «схлопывают» строки и детализация теряется.

Здесь на сцену выходят оконные функции. Их суперсила в том, что они производят вычисления над группой строк (окном), но не сливают их в одну, а добавляют результат как новый столбец к каждой исходной строке.

Проще на примере:
-- Обычная сумма возвращает одну строку
SELECT SUM(revenue) FROM orders;

-- Оконная сумма добавляет общий итог к КАЖДОЙ строке
SELECT
*,
SUM(revenue) OVER() AS total_revenue
FROM orders;


Аналогия:
Представьте, что вы учитель.

Обычный запрос - это выставить одну общую среднюю оценку всему классу.
Оконная функция - это рядом с каждой личной оценкой ученика написать средний балл по классу для сравнения.

Где использовать оконки?
При подсчете скользящего среднего, накопительных итогов, ранжирования, расчете долей, сравнения строк.

В следующем посте подробнее погрузимся в синтаксис и научимся создавать окна.

А пока поделитесь насколько сложно вам было осваивать оконные функции?

🤯 - сложно
🥱 - изи

#харды #sql
🥱45🤯17👍124🤔2
Учимся создавать «окна» в SQL

В прошлом посте мы узнали, зачем нужны оконные функции. Теперь научимся их объявлять. Все начинается с инструкции OVER() - она и определяет наше «окно».

Ключевые команды внутри OVER():

▪️PARTITION BY - разделяет данные на группы (партиции). Как GROUP BY, но без «схлопывания» строк. Считает функцию внутри каждой группы отдельно.
▪️ORDER BY - сортирует строки внутри окна. Критично для функций нарастающего итога, ранжирования и смещения (LAG/LEAD).

Разберем на примере простой таблички, содержащей дату, канал с которого пришел пользователь и количество конверсий:
SELECT 
date AS dt
, medium AS med
, conversions AS conv
, SUM(conversions) OVER(PARTITION BY date ORDER BY medium) AS sum
FROM orders


Что произойдет?

PARTITION BY Date создаст отдельное «окно» для каждой даты. Сумма будет считаться только в рамках одного дня.
ORDER BY medium отсортирует каналы внутри каждой даты.
SUM(conversions) в паре с ORDER BY рассчитает нарастающий итог конверсий внутри каждого дня. Для первой строки в окне (дне) sum будет равен ее conversions, для второй - сумме первой и второй, и так далее.

⚠️ Важно: ROWS / RANGE управляют диапазоном строк, по которым считается оконная функция. И даже если ничего не указывать, то по умолчанию используется RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW.

В результате выполнения запроса мы получим примерно такую табличку:
dt        med      conv  sum
10.05.20 cpa 1 1
10.05.20 cpc 2 3
10.05.20 organic 1 4
11.05.20 cpa 1 1
11.05.20 cpc 3 4
11.05.20 direct 1 5
11.05.20 organic 2 7
12.05.20 cpc 1 1
12.05.20 organic 2 3


Основы разобрали! Далее я расскажу как сужать фокус окна до «скользящего» диапазона с помощью ROWS BETWEEN.

#харды #sql
21👍14🤔1💯1
Как в SQL настроить фокус окна?

В прошлый раз мы научились делить данные на окна. Но что, если нам нужно посчитать результат не по всей группе, а сумму с соседней строкой?

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

Инструкция ROWS позволяет ограничить строки в окне, указывая фиксированное количество строк, предшествующих или следующих за текущей.

RANGE, в отличие от ROWS, работает не с физическими строками, а с диапазоном значений ORDER BY. Поэтому несколько строк с одинаковым значением могут попадать в одно окно.

Обе инструкции ROWS и RANGE всегда используются вместе с ORDER BY.

Возьмем наш пример и посчитаем сумму по текущей и следующей строке внутри каждого дня:

SELECT 
date
, medium
, conversions
, SUM(conversions) OVER(PARTITION BY date ORDER BY medium ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING) AS sum
FROM orders


Результат выполнения и логику запроса смотрите на картинке.

В выражении для ограничения окна также можно использовать следующие ключевые слова:

▪️UNBOUNDED PRECEDING - указывает, что окно начинается с первой строки группы;
▪️UNBOUNDED FOLLOWING - с помощью данной инструкции можно указать, что окно заканчивается на последней строке группы;
▪️CURRENT ROW - инструкция указывает, что окно начинается или заканчивается на текущей строке;
▪️BETWEEN «граница окна» AND «граница окна» - указывает нижнюю и верхнюю границу;
▪️«Значение» PRECEDING - определяет число строк перед текущей строкой (не допускается в RANGE);
▪️«Значение» FOLLOWING - определяет число строк после текущей строки (не допускается в RANGE).

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

Далее разберем самые полезные оконные функции.

#харды #sql
🔥273
Почему оконные функции могут тормозить запрос?

Я написал уже целую серию постов про оконки (раз, два, три) и там все выглядело красиво - добавил OVER() и получил профит. Но в комментах справедливо подметили, что оконные функции - это часто один из самых тяжелых видов запросов, который сильно жрет ресурсы.

Главный виновник тут обычно не сама функция, а сортировка. Сортировать много строк - дорогая операция на больших объемах данных, и ее лучше избегать везде, где она не нужна. Ниже 4 прикладных совета по оптимизации.

1⃣ Убери ORDER BY из окна, если порядок не влияет на смысл

Частая ошибка - писать ORDER BY внутри OVER() «на всякий случай». Как только он появляется, базе нужно обеспечить порядок строк, а это почти всегда дорого.

Если тебе нужен просто итог по группе (например, общий доход за день для каждой строки), а не накопительная сумма или скользящее окно - ORDER BY внутри окна не нужен.

-- Накопительная сумма (дороже)
SUM(revenue) OVER (PARTITION BY date ORDER BY created_at)

-- Просто итог по дню (дешевле)
SUM(revenue) OVER (PARTITION BY date)


2⃣ Чем меньше строк, тем быстрее

Оконки «проходят» по всем строкам, часто упорядочивают их, а иногда еще и держат большие куски данных в памяти. Поэтому самый простой ускоритель - сократить объем данных ДО оконки:

Отфильтруй период (например, последние 30/90 дней);
Не тащи лишние колонки («SELECT *» - плохо);
Если можно, то сначала агрегируй данные до нужной гранулярности, а окно считай уже на агрегате.

3⃣ Несколько разных окон = несколько сортировок

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

SUM(x) OVER (PARTITION BY a ORDER BY b) AS s1,
AVG(x) OVER (PARTITION BY a ORDER BY c) AS s2


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

4⃣ Используй план выполнения запроса

EXPLAIN - это команда, которая показывает план выполнения запроса: какие шаги база собирается сделать и где она потратит ресурсы. А EXPLAIN ANALYZE еще и выполняет запрос добавляя фактические цифры: сколько строк прошло через каждый шаг и сколько времени заняло.

Далее я разберу, как именно читать EXPLAIN и использовать эту команду для оптимизации.

#харды #sql
👍232🥱1