<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Оконные функции в SQL: простые примеры и применение для аналитики]]></title><description><![CDATA[<p dir="auto">Оконные функции в SQL — это мощный инструмент для анализа данных без потери строк в результате. Они позволяют вычислять агрегаты, ранжировать записи или брать значения из соседних строк в пределах заданного окна. С их помощью решается задача сложных вычислений, таких как скользящие средние или нумерация в группах.</p>
<p dir="auto">Это особенно полезно в аналитике, где GROUP BY сжимает данные, а оконные функции сохраняют каждую строку с дополнительными метриками. Вы разберётесь в синтаксисе, видах функций и реальных примерах на таблицах продаж. Такие приёмы упрощают запросы и делают код чище.</p>
<h2>Синтаксис и принцип работы оконных функций</h2>
<p dir="auto">Оконные функции работают с набором строк, называемым окном, которое определяется через OVER(). Окно делит данные на партиции по ключам, сортирует внутри них и может ограничивать рамку вычислений. В отличие от GROUP BY, где строки сжимаются в группы, оконные функции добавляют столбец с результатом к каждой исходной строке. Это позволяет видеть детали и итоги одновременно.</p>
<p dir="auto">Например, в таблице продаж по сотрудникам PARTITION BY разобьёт данные по employee_id, ORDER BY отсортирует по месяцам, а функция AVG посчитает среднее за окно. Такие запросы выполняются после WHERE и GROUP BY, но перед финальной сортировкой. Это даёт гибкость в анализе трендов без подзапросов или JOIN.</p>
<p dir="auto">Вот базовый шаблон:</p>
<pre><code class="language-sql">&lt;функция&gt;(столбец) OVER (
    PARTITION BY столбец_группировки
    ORDER BY столбец_сортировки
    ROWS BETWEEN ... 
)
</code></pre>
<ul>
<li><strong>PARTITION BY</strong>: делит на группы, как GROUP BY, но без сжатия строк.</li>
<li><em>ORDER BY в OVER</em>: определяет порядок внутри партиции, обязательно для ранжирующих функций.</li>
<li><strong>ROWS или RANGE</strong>: задаёт рамку окна, например, последние 3 строки или текущую и предыдущую.</li>
</ul>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Параметр</th>
<th>Описание</th>
<th>Пример</th>
</tr>
</thead>
<tbody>
<tr>
<td>PARTITION BY</td>
<td>Группировка</td>
<td>PARTITION BY department</td>
</tr>
<tr>
<td>ORDER BY</td>
<td>Сортировка в окне</td>
<td>ORDER BY sale_date</td>
</tr>
<tr>
<td>ROWS</td>
<td>Рамка строк</td>
<td>ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING</td>
</tr>
</tbody>
</table>
<h2>Виды оконных функций и их применение</h2>
<p dir="auto">Оконные функции делятся на агрегатные, ранжирующие, смещения и другие. Агрегатные вроде SUM или AVG считают итоги по окну, ранжирующие присваивают номера, а смещения берут данные из соседних строк. Это упрощает задачи вроде расчёта процентов от общего или поиска лидеров в группах.</p>
<p dir="auto">Возьмём таблицу sport_sales с полями sale_date, department, sales. Агрегатная функция покажет сумму продаж по отделу для каждой строки, ранжирующая — место отдела по продажам на дату. Такой подход заменяет сложные подзапросы одним SELECT.</p>
<ul>
<li><strong>Агрегатные</strong>: SUM(sales) OVER() — общая сумма по всем строкам.</li>
<li><strong>Ранжирующие</strong>: ROW_NUMBER() — уникальный номер строки.</li>
<li><strong>Смещения</strong>: LAG(sales) — значение из предыдущей строки.</li>
</ul>
<p dir="auto">Пример агрегатной:</p>
<pre><code class="language-sql">SELECT 
    department, 
    sales, 
    SUM(sales) OVER (PARTITION BY department) AS dept_total
FROM sport_sales;
</code></pre>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Вид</th>
<th>Функции</th>
<th>Задача</th>
</tr>
</thead>
<tbody>
<tr>
<td>Агрегатные</td>
<td>SUM, AVG, COUNT</td>
<td>Скользящие итоги</td>
</tr>
<tr>
<td>Ранжирующие</td>
<td>ROW_NUMBER, RANK</td>
<td>Топ-N в группах</td>
</tr>
<tr>
<td>Смещения</td>
<td>LAG, LEAD, FIRST_VALUE</td>
<td>Сравнение с соседями</td>
</tr>
</tbody>
</table>
<h2>Практические примеры на реальных данных</h2>
<p dir="auto">Рассмотрим таблицу employee_sales: employee_id, month, sales_amount. Задача — скользящее среднее за 3 месяца и ранг лучших продаж. Оконная функция AVG с ROWS BETWEEN 2 PRECEDING AND CURRENT ROW посчитает среднее по трём строкам, RANK покажет место месяца для сотрудника.</p>
<p dir="auto">Это полезно для отчётов: видно не только итоги, но и динамику. Без окон пришлось бы использовать подзапросы или CTE, что усложняет код. Функция LAG добавит предыдущий месяц для сравнения роста.</p>
<p dir="auto">Примеры запросов:</p>
<ol>
<li>Скользящее среднее:</li>
</ol>
<pre><code class="language-sql">SELECT 
    employee_id, 
    month, 
    sales_amount,
    AVG(sales_amount) OVER (
        PARTITION BY employee_id 
        ORDER BY month 
        ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
    ) AS moving_avg
FROM employee_sales;
</code></pre>
<ol start="2">
<li>Ранг продаж:</li>
</ol>
<pre><code class="language-sql">SELECT 
    employee_id, 
    month, 
    sales_amount,
    RANK() OVER (
        PARTITION BY employee_id 
        ORDER BY sales_amount DESC
    ) AS sales_rank
FROM employee_sales;
</code></pre>
<ul>
<li><strong>LAG/LEAD</strong>: для A/B-тестов или трендов, <em>игнорирует NULL в дефолте</em>.</li>
<li><em>FILTER в агрегатах</em>: SUM(sales) FILTER (WHERE sales &gt; 100) — только большие сделки.</li>
<li><strong>NTILE(4)</strong>: делит на квартили для анализа распределения.</li>
</ul>
<table class="table table-bordered table-striped">
<thead>
<tr>
<th>Функция</th>
<th>Результат</th>
<th>Полезно для</th>
</tr>
</thead>
<tbody>
<tr>
<td>ROW_NUMBER()</td>
<td>1,2,3…</td>
<td>Уникальная нумерация</td>
</tr>
<tr>
<td>DENSE_RANK()</td>
<td>1,1,2…</td>
<td>Ранг без пропусков</td>
</tr>
<tr>
<td>LAG(value,1)</td>
<td>Предыдущее</td>
<td>Дельта роста</td>
</tr>
</tbody>
</table>
<h2>Расширенные приёмы с FILTER и фреймами</h2>
<p dir="auto">Фреймы ROWS или RANGE уточняют, какие строки входят в расчёт: от начала окна, только текущая или с соседями. FILTER добавляет условие внутри OVER, как WHERE для агрегатов. Это решает нишевые задачи, вроде суммы только положительных продаж.</p>
<p dir="auto">В таблице orders с order_date, category, amount LEAD(amount,1) OVER() покажет следующий заказ, FIRST_VALUE — первый в группе. Такие функции экономят на JOIN и делают запросы быстрее.</p>
<p dir="auto">Пример с FILTER:</p>
<pre><code class="language-sql">SELECT 
    sale_date, 
    department, 
    sales,
    SUM(sales) FILTER (WHERE sales &gt; 100) 
    OVER (PARTITION BY department) AS big_sales
FROM sport_sales;
</code></pre>
<ul>
<li><em>RANGE vs ROWS</em>: RANGE учитывает равные значения по ORDER BY.</li>
<li><strong>ROWS UNBOUNDED PRECEDING</strong>: кумулятивная сумма от начала.</li>
<li>Используйте в ORDER BY для сортировки по оконным результатам.</li>
</ul>
<h2>Оконные функции в сложных сценариях анализа</h2>
<p dir="auto">Оконные функции идеальны для дашбордов: кумулятивные суммы, проценты от ряда, топ-N с детализацией. Они работают после GROUP BY, добавляя ранги к сгруппированным данным. В T-SQL и PostgreSQL поддержка полная, с нюансами в фреймах.</p>
<p dir="auto">Остаётся место для оптимизации: избегайте больших окон на миллионах строк, комбинируйте с индексами. Стоит поэкспериментировать с NTILE для сегментации или CUME_DIST для распределений — это расширит аналитику без дополнительных таблиц.</p>
]]></description><link>https://forum.exlends.ru/topic/619/okonnye-funkcii-v-sql-prostye-primery-i-primenenie-dlya-analitiki</link><generator>RSS for Node</generator><lastBuildDate>Sat, 17 Oct 2026 03:07:34 GMT</lastBuildDate><atom:link href="https://forum.exlends.ru/topic/619.rss" rel="self" type="application/rss+xml"/><pubDate>Sat, 21 Feb 2026 05:35:14 GMT</pubDate><ttl>60</ttl></channel></rss>