Over sql partition by: OVER, предложение (Transact-SQL) — SQL Server

Оконные функции в SQL — что это и зачем они нужны

Многие разработчики, даже давно знакомые с SQL, не понимают оконные функции, считая их какой-то особой магией для избранных. И, хотя реализация оконных функций поддерживается с SQL Server 2005, кто-то до сих пор «копипастит» их со StackOverflow, не вдаваясь в детали. Этой статьёй мы попытаемся развенчать миф о неприступности этой функциональности SQL и покажем несколько примеров работы оконных функций на реальном датасете.

Почему не GROUP BY и не JOIN

Сразу проясним, что оконные функции — это не то же самое, что GROUP BY. Они не уменьшают количество строк, а возвращают столько же значений, сколько получили на вход. Во-вторых, в отличие от GROUP BY, OVER может обращаться к другим строкам. И в-третьих, они могут считать скользящие средние и кумулятивные суммы.

Примечание Оконные функции не изменяют выборку, а только добавляют некоторую дополнительную информацию о ней. Для простоты понимания можно считать, что SQL сначала выполняет весь запрос (кроме сортировки и limit), а уже потом считает значения окна.

Окей, с GROUP BY разобрались. Но в SQL практически всегда можно пойти несколькими путями. К примеру, может возникнуть желание использовать подзапросы или JOIN. Конечно, JOIN по производительности предпочтительнее подзапросов, а производительность конструкций JOIN и OVER окажется одинаковой. Но OVER даёт больше свободы, чем жёсткий JOIN. Да и объём кода в итоге окажется гораздо меньше.

Для начала

Оконные функции начинаются с оператора OVER и настраиваются с помощью трёх других операторов: PARTITION BY, ORDER BY и ROWS. Про ORDER BY, PARTITION BY и его вспомогательные операторы LAG, LEAD, RANK мы расскажем подробнее.
Все примеры будут основаны на датасете олимпийских медалистов от Datacamp. Таблица называется summer_medals и содержит результаты Олимпиад с 1896 по 2010:

ROW_NUMBER и ORDER BY

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

SELECT
athlete,
event,
ROW_NUMBER() OVER() AS row_number
FROM Summer_Medals
ORDER BY row_number ASC;

Каждая пара «спортсмен — вид спорта» получила номер, причём к этим номерам можно обращаться по имени row_number.
ROW_NUMBER можно объединить с ORDER BY, чтобы определить, в каком порядке строки будут нумероваться. Выберем с помощью DISTINCT все имеющиеся виды спорта и пронумеруем их в алфавитном порядке:

SELECT
sport,
ROW_NUMBER() OVER(ORDER BY sport ASC) AS Row_N
FROM (
SELECT DISTINCT sport
FROM Summer_Medals
) AS sports
ORDER BY sport ASC;

PARTITION BY и LAG, LEAD и RANK

PARTITION BY позволяет сгруппировать строки по значению определённого столбца. Это полезно, если данные логически делятся на какие-то категории и нужно что-то сделать с данной строкой с учётом других строк той же группы (скажем, сравнить теннисиста с остальными теннисистами, но не с бегунами или пловцами). Этот оператор работает только с оконными функциями типа LAG, LEAD, RANK и т. д.

LAG

Функция LAG берёт строку и возвращает ту, которая шла перед ней. Например, мы хотим найти всех олимпийских чемпионов по теннису (мужчин и женщин отдельно), начиная с 2004 года, и для каждого из них выяснить, кто был предыдущим чемпионом.
Решение этой задачи требует нескольких шагов. Сначала надо создать табличное выражение, которое сохранит результат запроса «чемпионы по теннису с 2004 года» как временную именованную структуру для дальнейшего анализа. А затем разделить их по полу и выбрать предыдущего чемпиона с помощью LAG:

-- Табличное выражение ищет теннисных чемпионов и выбирает нужные столбцы
WITH Tennis_Gold AS (
SELECT
Athlete,
Gender,
Year,
Country
FROM
Summer_Medals
WHERE
Year >= 2004 AND
Sport = 'Tennis' AND
event = 'Singles' AND
Medal = 'Gold')
-- Оконная функция разделяет по полу и берёт чемпиона из предыдущей строки
SELECT
Athlete as Champion,
Gender,
Year,
LAG(Athlete) OVER (PARTITION BY gender
ORDER BY Year ASC) AS Last_Champion
FROM Tennis_Gold
ORDER BY Gender ASC, Year ASC;

Функция PARTITION BY в таблице вернула сначала всех мужчин, потом всех женщин. Для победителей 2008 и 2012 года приведён предыдущий чемпион; так как данные есть только за 3 олимпиады, у чемпионов 2004 года нет предшественников, поэтому в соответствующих полях стоит null.

LEAD

Функция LEAD похожа на LAG, но вместо предыдущей строки возвращает следующую. Можно узнать, кто стал следующим чемпионом после того или иного спортсмена:

-- Табличное выражение ищет теннисных чемпионов и выбирает нужные столбцы
WITH Tennis_Gold AS (
SELECT
Athlete,
Gender,
Year,
Country
FROM
Summer_Medals
WHERE
Year >= 2004 AND
Sport = 'Tennis' AND
event = 'Singles' AND
Medal = 'Gold')
-- Оконная функция разделяет по полу и берёт чемпиона из следующей строки
SELECT
Athlete as Champion,
Gender,
Year,
LEAD(Athlete) OVER (PARTITION BY gender
ORDER BY Year ASC) AS Future_Champion
FROM Tennis_Gold
ORDER BY Gender ASC, Year ASC;

RANK

Оператор RANK похож на ROW_NUMBER, но присваивает одинаковые номера строкам с одинаковыми значениями, а «лишние» номера пропускает. Есть также DENSE_RANK, который не пропускает номеров. Звучит запутанно, так что проще показать на примере. Вот ранжирование стран по числу олимпиад, в которых они участвовали, разными операторами:

  • Row_number — ничего интересного, строки просто пронумерованы по возрастанию.
  • Rank_number — строки ранжированы по возрастанию, но нет номера 3. Вместо этого, 2 строки делят номер 2, а за ними сразу идёт номер 4.
  • Dense_rank — то же самое, что и rank_number, но номер 3 не пропущен. Номера идут подряд, но зато никто не оказался пятым из пяти.

Вот код:

-- Табличное выражение выбирает страны и считает годы
WITH countries AS (
SELECT
Country,
COUNT(DISTINCT year) AS participated
FROM
Summer_Medals
WHERE
Country in ('GBR', 'DEN', 'FRA', 'ITA','AUT')
GROUP BY
Country)

-- Разные оконные функции ранжируют страны
SELECT
Country,
participated,
ROW_NUMBER()
OVER(ORDER BY participated DESC) AS Row_Number,
RANK()
OVER(ORDER BY participated DESC) AS Rank_Number,
DENSE_RANK()
OVER(ORDER BY participated DESC) AS Dense_Rank
FROM countries
ORDER BY participated DESC;

Напоследок

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

Конечно, это далеко не все возможности оконных функций. Для них есть много других полезных вещей, например ROWS, NTILE и агрегирующие функции (SUM, MAX, MIN и другие), но об этом поговорим в другой раз.

Адаптированный перевод статьи «Intro to Window Functions in SQL»

Полезные оконные функции SQL — Разработка на vc.ru

Можно бесконечно долго «воротить нос» от использования SQL для Data Preparation, отдавая лавры змеиному языку, но нельзя не признавать факт, что чаще мы используем и еще долго будем использовать SQL для работы с данными, в том числе и очень объемными.

34 557
просмотров

Более того, считаем, что на текущий момент SQL окажется под рукой сотрудника с большей вероятностью, чем Python, и поможет быстро решить аналитическую задачку с приоритетом «-1».

Предложение OVER помогает «открыть окно», т. е. определить строки, с которым будет работать та или иная функция.

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

ORDER BY определит порядок обработки строк.

В одном select может быть больше одного OVER, эта прекрасная особенность упростит выполнение аналитической задачи в дальнейшем.

Итак, оконные функции делятся на:

  • Агрегатные функции
  • Ранжирующие функции
  • Функции смещения
  • Аналитические функции

Собственно, те же, что и обычные, только встроенные в конструкцию с OVER

SUM/ AVG / COUNT/ MIN/ MAX

Для наглядности работы данных функций воспользуемся базовым набором данных (T)

Задача:

Найти максимальную задолженность в каждом банке.

Для чего тут оконные функции? Можно же просто написать:

SELECT TB, max(OSZ) OSZ
FROM T
group by TB

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

Собрать дэшборд, в котором содержится информация о максимальной задолженности в каждом банке, а также средний размер процентной ставки в каждом банке в зависимости от сегмента, плюс еще количество договоров всего всем банкам (в голове рисуются множественные джойны из подзапросов и как-то сразу тяжело на душе). Однако, как я говорил выше, в одном select можно использовать много OVER, а также еще один прекрасный факт: набор строк в окне, связывается с текущей строкой, а не с группой агрегированных. Таким образом:

SELECT TB, ID_CLIENT, ID_DOG, OSZ, PROCENT_RATE, RATING, SEGMENT
, MAX(OSZ) OVER (PARTITION BY TB) ‘Максимальная задолженность в разбивке по банкам’
, AVG(PROCENT_RATE) OVER (PARTITION BY TB, SEGMENT) ‘Средняя процентная ставка в разрезе банка и сегмента’
, COUNT(ID_DOG) OVER () ‘Всего договоров во всех банках’
FROM T

На примере AVG(procent_RATE) OVER (partition BY TB, segment) подробнее:

  • Мы применяем AVG – агрегатную функцию по подсчету среднего значения к столбцу procent_RATE.
  • Затем предложением OVER определяем, что будем работать с некоторым набором строк. По умолчанию, если указать OVER() с пустыми строками, то этот набор строк равен всей таблице.
  • Предложением partition BY выделяем разделы в наборе строк по заданному условию, в нашем случае, в разбивке на Территориальные банки и Сегмент.
  • В итоге, к каждой строке базовой таблицы применится функция по подсчету среднего из набора строк, разбитых на разделы (по Территориальным Банкам и Сегменту).

Другой тип оконных функций, надо признать, мой любимый и был использован для решения многих задач. Функции ранжирования для каждой строки в разделе возвращают значение рангов или рейтингов. Все ведь любят рейтинги, правда…?

Базовый набор данных: банки, отделы и количество ревизий.

Сами ранжирующие функции:

ROW_number – нумерует строки в результирующем наборе.

RANK -присваивает ранг для каждой строки, если найдутся одинаковые значения, то следующий ранг присваивается с пропуском.

DENSE_RANK -присваивает ранг для каждой строки, если найдутся одинаковые значения, то следующий ранг присваивается без пропуска.

NTILE – помогает разделить результирующий набор на группы.

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

SELECT *
, ROW_NUMBER() OVER(ORDER BY count_revisions desc)
, Rank() OVER(ORDER BY count_revisions desc)
, DENSE_RANK() OVER(ORDER BY count_revisions desc)
, NTILE(3) OVER(ORDER BY count_revisions desc)
FROM Table_Rev

ROW_number – пронумеровал столбцы в порядке убывания количества ревизий.

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

DENSE_RANK – аналогично RANK, но как только встретились одинаковые значения, следующее значение получило ранг 5.

NTILE – функция помогла разбить таблицу на 3 группы (указал в аргументе). Так как в таблице 18 значений, в каждую группу попало по 6.

Задача:

Найти второй отдел во всех банках по количеству ревизий.

Можно, конечно, воспользоваться чем-то вроде:

SELECT MAX(count_revisions) ms
FROM Table_Rev
WHERE count_revisions!=(SELECT MAX(count_revisions) FROM Table_Rev)

Но если речь идет не про второй отдел, а про трети? .. уже сложнее. Действительно, никто не списывает со счетов offset, но в этой статье говорится об оконных функциях, так почему бы не написать так:

With T_R as
(
SELECT *
, DENSE_RANK() OVER(ORDER BY count_revisions desc) ds
FROM Table_Rev
)
SELECT * FROM T_R
WHERE ds=3

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

With T_R as
(
SELECT *
, DENSE_RANK() OVER(PARTITION BY tb ORDER BY count_revisions) ds
FROM Table_Rev
)
SELECT tb,dep,count_revisions
FROM T_R
WHERE ds=1

Получаем:

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

LAG — смещение назад.

LEAD — смещение вперед.

FIRST_VALUE — найти первое значение набора данных.

LAST_VALUE — найти последнее значение набора данных.

LAG и LEAD имеют следующие аргументы:

  • Столбец, значение которого необходимо вернуть
  • На сколько строк выполнить смешение (дефолт =1)
  • Что вставить, если вернулся NULL

Как обычно, на практике проще:

Базовый набор данных, содержит id задачи, события внутри нее и их дату:

Применяя конструкцию:

SELECT *
, LEAD (Event, 1, ‘end’) OVER (PARTITION BY ID_Task ORDER BY Date_Event) as Next_Event
, LEAD (Date_Event, 1, ‘2099-01-01’) OVER(PARTITION BY ID_Task ORDER BY Date_Event) as Next_Date
FROM Table_Task

Получаем набор данных, который хоть сейчас в graphviz (нет).

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

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

Обзор предложения SQL PARTITION BY

В этой статье будет рассмотрено предложение SQL PARTITION BY и, в частности, разница с GROUP BY в операторе select. Мы также рассмотрим различные варианты использования SQL PARTITION BY.

Мы используем SQL PARTITION BY, чтобы разделить результирующий набор на разделы и выполнить вычисления для каждого подмножества разделенных данных.

Подготовка данных образца

Давайте создадим таблицу Orders в моей тестовой базе данных 9.0009 SQLShackDemo и вставьте записи для написания дальнейших запросов.

1

2

3

4

5

6

7

8

10 3

9

3

Используйте SQLSHACKDEMO

GO

CREATE TABLE [DBO]. [Заказы]

(

[ORDERID] INT,

[ORDERDATE] Дата,

[Customername] VARCHAR (100),

    [Customercity] VARCHAR(100),

    [Сумма заказа] MONEY

)

Я использую ApexSQL Generate для вставки примеров данных в эту статью. Щелкните правой кнопкой мыши таблицу Orders и Generate test data .

Он запускает ApexSQL Generate. Я создал скрипт для вставки данных в таблицу Orders. Выполните этот скрипт, чтобы вставить 100 записей в таблицу Orders.

1

2

3

4

5

6

7

USE [SQLShackDemo]

GO

INSERT [dbo].[Orders]  VALUES (216090, CAST(N’1826-12-19′ AS Date), N’Edward’, N’Phoenix’, 4713. 8900)

GO

INSERT [dbo].[Orders]  VALUES (508220, CAST(N’1826-12-09′ AS Date), N’Aria’, N’San Francisco’, 9832.7200)

GO

Как только мы выполним операторы вставки, мы увидим данные в таблице Orders на следующем изображении.

Мы используем предложение SQL GROUP BY для группировки результатов по указанному столбцу и используем агрегатные функции, такие как Avg(), Min(), Max(), для вычисления необходимых значений.

Группировать по синтаксису функций

Выражение SELECT, агрегатная функция ()

ИЗ таблиц

WHERE условия

GROUP BY выражение

Предположим, мы хотим найти следующие значения в таблице Orders

  • Минимальная стоимость заказа в городе
  • Максимальная стоимость заказа в городе
  • Средняя стоимость заказа в городе

Выполните следующий запрос с предложением GROUP BY, чтобы вычислить эти значения.

1

2

3

4

5

6

Выберите CustomerCity,

AVG (OrderAmount) в качестве Avgorderamount,

мин (orderAmount) в качестве MinOrderAmount,

Sum (OrderAmount) TotalOrderAnt

от [DBO]. [Orders]

Группа по CustomerTICTION;

На следующем снимке экрана мы видим среднее, минимальное и максимальное значения, сгруппированные по CustomerCity.

Теперь мы хотим добавить в вывод столбцы CustomerName и OrderAmount . Давайте добавим эти столбцы в оператор select и выполним следующий код.

1

2

3

4

5

6

SELECT Customercity, CustomerName ,OrderAmount,

       AVG(Orderamount) AS AvgOrderAmount,

       MIN(СуммаЗаказа) AS MinOrderAmount,

       СУММА(СуммаЗаказа) TotalOrderAmount

ОТ [dbo]. [Заказы]

ГРУППА ПО Customercity;

Как только мы выполним этот запрос, мы получим сообщение об ошибке. В предложении SQL GROUP BY мы можем использовать столбец в операторе select, если он также используется в предложении Group by. В предложении select не разрешен ни один столбец, который не является частью предложения GROUP BY.

Мы можем использовать предложение SQL PARTITION BY для решения этой проблемы. Давайте рассмотрим это подробнее в следующем разделе.

РАЗДЕЛ SQL ПО

Мы можем использовать предложение SQL PARTITION BY с предложением OVER , чтобы указать столбец, для которого нам нужно выполнить агрегирование. В предыдущем примере мы использовали столбец Group By with CustomerCity и рассчитали среднее, минимальное и максимальное значения.

Давайте повторно запустим этот сценарий с предложением SQL PARTITION BY, используя следующий запрос.

Выберите CustomerCity,

AVG (OrderAmount) Over (раздел CustomerCity) как Avgorderamount,

мин (OrderAmount). ОТ [dbo].[Заказы];

На выходе мы получаем агрегированные значения, аналогичные предложению GROUP By. Вы можете заметить разницу в выводе предложений SQL PARTITION BY и GROUP BY.

Группа по

РАЗДЕЛ SQL ПО

Мы получаем ограниченное количество записей, используя предложение Group By

Мы получаем все записи в таблице, используя предложение PARTITION BY.

Это дает одну строку на группу в наборе результатов. Например, мы получаем результат для каждой группы CustomerCity в предложении GROUP BY.

Он дает агрегированные столбцы с каждой записью в указанной таблице.

У нас есть 15 записей в таблице Orders. В выводе запроса SQL PARTITION BY мы также получаем 15 строк вместе с минимальными, максимальными и средними значениями.

В предыдущем примере мы получаем сообщение об ошибке, если пытаемся добавить столбец, который не является частью предложения GROUP BY.

Мы можем добавить необходимые столбцы в оператор select с помощью предложения SQL PARTITION BY. Давайте добавим столбцы CustomerName и OrderAmount и выполним следующий запрос.

1

2

3

4

5

6

7

Выберите CustomerCity,

Customername,

OrderAmount,

AVG (OrderAmount) Over (раздел по CustomerCity) как AvgorderAmount,

мин (orderAmount). (РАЗДЕЛ ПО Customercity) TotalOrderAmount

ОТ [dbo].[Заказы];

Мы получаем столбцы CustomerName и OrderAmount вместе с выходными данными агрегированной функции. Мы также получаем все строки, доступные в таблице Orders.

На следующем снимке экрана вы можете увидеть CustomerCity Chicago , он выполняет агрегирование (Avg, Min и Max) и дает значения в соответствующих столбцах.

Точно так же мы можем использовать другие агрегатные функции, такие как count, чтобы узнать общее количество заказов в конкретном городе с предложением SQL PARTITION BY .

1

2

3

4

5

6

7

8

SELECT Customercity,

       CustomerName,

       OrderAmount,

       COUNT(OrderID) OVER(PARTITION BY Customercity) AS CountOfOrders,

AVG (OrderAmount) Over (раздел по Customercity) как Avgorderamount,

мин (OrderAmount). [Заказы];

Мы можем видеть количество заказов для определенного города. Например, у нас есть два заказа из города Остин, поэтому; он показывает значение 2 в столбце CountofOrders .

Предложение PARTITION BY с ROW_NUMBER()

Мы можем использовать предложение SQL PARTITION BY с функцией ROW_NUMBER(), чтобы иметь номер строки для каждой строки. Мы определяем следующие параметры для использования ROW_NUMBER с предложением SQL PARTITION BY.

  • РАЗДЕЛЕНИЕ ПО столбцу — в этом примере мы хотим разделить данные по столбцу CustomerCity
  • Порядок: В столбце ORDER BY мы определяем столбец или условие, которое определяет номер строки. В этом примере мы хотим отсортировать данные по OrderAmount столбец

1

2

3

4

5

6

7

8

10 3

9

3

Выберите CustomerCity,

CustomerName,

ROW_NUMBER () OUR (разделение по CustomerCity

Заказ по OrderAmount Disc) как «номер строки»,

OrderAmount,

Count (OrderId). ,

AVG (OrderAmount) Over (раздел по Customercity) как Avgorderamount,

мин (OrderAmount). [Заказы];

На следующем снимке экрана мы видим для CustomerCity Chicago , у нас есть строка номер 1 для заказа с наибольшей суммой 7577,90. он предоставляет номер строки с убывающим значением OrderAmount.

Предложение PARTITION BY с совокупным общим значением

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

Например, в городе Чикаго у нас четыре заказа.

CustomerCity

Имя Клиента

Классифицировать

Сумма заказа

Совокупное общее количество строк

Нарастающий итог

Чикаго

Марвин

1

7577,9

Ранг 1 +2

14777,51

Чикаго

Лоуренс

2

7199,61

Ранг 2+3

14047. 21

Чикаго

Алекс

3

6847,66

Ранг 3+4

8691,49

Чикаго

Джером

4

1843,83

Ранг 4

1843,83

В следующем запросе мы указали предложение ROWS для выбора текущей строки (используя CURRENT ROW) и следующую строку (используя 1 FOLLOWING). Далее он вычисляет сумму по этим строкам, используя sum(Orderamount) с разделением на CustomerCity (используя OVER(PARTITION BY Customercity ORDER BY OrderAmount DESC).

1

2

3

4

5

6

7

Выберите CustomerCity,

CustomerName,

OrderAmount,

ROW_NUMBER () OUT (раздел CustomerCity

Заказ по порядку DESC) в качестве «номера строки»,

Конвертируйте (VARCHAR (20), сумма (OrderAmount). (РАЗДЕЛЕНИЕ ПО Customercity

       ORDER BY OrderAmount DESC ROWS МЕЖДУ ТЕКУЩЕЙ СТРОКОЙ И 1 СЛЕДУЮЩЕЙ), 1) AS CumulativeTotal,

Точно так же мы можем вычислить кумулятивное среднее, используя следующий запрос с предложением SQL PARTITION BY .

1

2

3

4

5

6

7

SELECT Customercity,

       CustomerName,

       OrderAmount,

       ROW_NUMBER() OVER(РАЗДЕЛ ПО Customercity

       ORDER BY OrderAmount DESC) AS «Номер строки»,

       CONVERT(VARCHAR(20), AVG(orderamount) OVER(PARTITION BY Customercity

       ORDER BY OrderAmount) ASMULTIVE ROWS IN 1 DESC ROWS IN 1 CURRENT

ROWS UNBOUNDED PRECEDING с пунктом PARTITION BY

Мы можем использовать ROWS UNBOUNDED PRECEDING с предложением SQL PARTITION BY, чтобы выбрать строку в разделе перед текущей строкой и строку с наибольшим значением после текущей строки.

В следующей таблице мы видим для строки 1; в этом разделе нет ни одной строки с высоким значением. Таким образом, кумулятивное среднее значение такое же, как и в строке 1 OrderAmount.

Для Row2 выполняется поиск текущего значения строки (7199,61) и строки с наибольшим значением 1 (7577,9). Он вычисляет среднее значение для этих двух сумм.

Для строки 3 выполняется поиск текущего значения (6847,66) и более высокого значения суммы, чем это значение, то есть 7199,61 и 7577,9.0. Он вычисляет среднее из них и возвращает результат.

CustomerCity

Имя Клиента

Классифицировать

Сумма заказа

Совокупное среднее количество строк

Совокупное среднее

Чикаго

Марвин

1

7577,9

Ранг 1

7577,90

Чикаго

Лоуренс

2

7199,61

Ранг 1+2

7388,76

Чикаго

Алекс

3

6847,66

Ранг 1+2+3

7208,39

Чикаго

Джером

4

1843,83

Ранг 1+2+3+4

5867,25

Выполните следующий запрос, чтобы получить этот результат с нашими примерами данных.

1

2

3

4

5

6

7

8

SELECT Customercity,

       CustomerName,

       OrderAmount,

       ROW_NUMBER() OVER(РАЗДЕЛ ПО Customercity

Заказ по OrderAmount desc) как «номер строки»,

преобразовать (varchar (20), avg (orderamount) над (раздел от Customercity

Порядок по orderAmount descept nucouded), 1) как cumulativeavg

dbo].[Заказы];

Заключение

В этой статье мы рассмотрели предложение SQL PARTIION BY и его сравнение с предложением GROUP BY. Мы также изучили его использование на нескольких примерах. Я надеюсь, что вы найдете эту статью полезной и не стесняйтесь задавать любые вопросы в комментариях ниже.

  • Автор
  • Последние сообщения

Раджендра Гупта

Привет! Я Раджендра Гупта, специалист по базам данных и архитектор, помогаю организациям быстро и эффективно внедрять решения Microsoft SQL Server, Azure, Couchbase, AWS, устранять связанные проблемы и настраивать производительность с более чем 14-летним опытом.

Я автор книги «DP-300 Administering Relational Database on Microsoft Azure». Я опубликовал более 650 технических статей о MSSQLTips, SQLShack, Quest, CodingSight и MultipleNines.

Я создатель одной из крупнейших бесплатных онлайн-коллекций статей по одной теме, включая серию из 50 статей о группах доступности SQL Server Always On.

Основываясь на моем вкладе в сообщество SQL Server, я неоднократно признавался престижным Лучшим автором года в 2019, 2020 и 2021 годах (2-е место) в SQLShack и наградой чемпионов MSSQLTIPS в 2020 году.

Личный блог: https://www.dbblogger.com
Меня всегда интересуют новые задачи, поэтому, если вам нужна консультационная помощь, свяжитесь со мной по адресу [email protected]

Просмотреть все сообщения Раджендры Гупты

Последние сообщения Раджендры Гупты (посмотреть все)

SQL PARTITION BY Пункт: Когда и как использовать — Управление базами данных — Блоги

В этой статье мы рассмотрим, когда и как использовать предложение SQL PARTITION BY и сравнить его с использованием предложения GROUP BY.

Понимание функции окна

Пользователи баз данных используют агрегатные функции, такие как MAX(), MIN(), AVERAGE() и COUNT(), для выполнения анализа данных. Эти функции работают со всей таблицей и возвращают отдельные агрегированные данные с помощью предложения GROUP BY. Иногда нам требуются агрегированные значения по небольшому набору строк. В этом случае оконная функция в сочетании с агрегатной функцией помогает достичь желаемого результата. Функция Window использует предложение OVER() и может включать следующие функции:

  • Разделить по:  Разделяет строки или набор результатов запроса на небольшие разделы.
  • Упорядочить по:  Упорядочивает строки в возрастающем или убывающем порядке для окна раздела. Порядок по умолчанию — возрастающий.
  • Строка или диапазон:  Вы можете дополнительно ограничить количество строк в разделе, указав начальную и конечную точки.

В этой статье мы сосредоточимся на изучении предложения SQL PARTITION BY.

Подготовка данных образца

Предположим, у нас есть таблица [SalesLT].[Orders], в которой хранится информация о заказах клиентов. В нем есть столбец [Город], в котором указан город клиента, в котором был размещен заказ.

 СОЗДАТЬ ТАБЛИЦУ [SalesLT].[Заказы]
(
порядковый номер INT,
дата заказа ДАТА,
имя_клиента VARCHAR(100),
Город ВАРЧАР(50),
сумма ДЕНЬГИ
)
ВСТАВЬТЕ В [SalesLT].[Заказы]
ВЫБЕРИТЕ 1, '01.01.2021', 'Мохан Гупта', 'Алвар', 10000
СОЮЗ ВСЕХ
ВЫБЕРИТЕ 2, '04.02.2021', 'Счастливчик Али', 'Кота', 20000
СОЮЗ ВСЕХ
ВЫБЕРИТЕ 3, '02.03.2021', 'Радж Кумар', 'Джайпур', 5000
СОЮЗ ВСЕХ
ВЫБЕРИТЕ 4, 02.04.2021, Джйоти Кумари, Джайпур, 15000
СОЮЗ ВСЕХ
ВЫБЕРИТЕ 5, '03.05.2021', 'Рахул Гупта', 'Джайпур', 7000
СОЮЗ ВСЕХ
ВЫБЕРИТЕ 6, '04.06.2021', 'Мохан Кумар', 'Алвар', 25000
СОЮЗ ВСЕХ
ВЫБЕРИТЕ 7, '02.07.2021', 'Кашиш Агарвал', 'Алвар', 15000
СОЮЗ ВСЕХ
ВЫБЕРИТЕ 8, «03.08.2021», «Нагар Сингх», «Кота», 2000 г.
СОЮЗ ВСЕХ
ВЫБЕРИТЕ 9,'04.09.2021','Анил КГ','Алвар',1000
Go 

Допустим, мы хотим узнать общую стоимость заказов по местоположению (Город). Для этого мы используем функции SUM() и GROUP BY, как показано ниже.

 ВЫБЕРИТЕ город КАК CustomerCity
,sum(amount) AS totalamount FROM [SalesLT].[Orders]
СГРУППИРОВАТЬ ПО ГОРОДУ
ORDER BY city 

В результирующем наборе мы не можем использовать неагрегированные столбцы в инструкции SELECT. Например, мы не можем отобразить [CustomerName] в выходных данных, поскольку оно не включено в предложение GROUP BY.

SQL Server выдает следующее сообщение об ошибке при попытке использовать неагрегированный столбец в списке столбцов.

 ВЫБЕРИТЕ город КАК CustomerCity, CustomerName, сумма,
SUM(сумма) OVER(РАЗБИВКА ПО городам) TotalOrderAmount
FROM [SalesLT].[Orders] 

Как показано ниже, предложение PARTITION BY создает меньшее окно (набор строк данных), выполняет агрегирование и отображает его. Вы также можете просмотреть неагрегированные столбцы в этом выводе.

Точно так же вы можете использовать функции AVG(), MIN(), MAX() для вычисления среднего, минимального и максимального количества строк в окне.

 ВЫБЕРИТЕ город КАК CustomerCity, CustomerName, сумма,
СУММА(сумма) ПРЕВЫШЕНИЕ(РАЗДЕЛ ПО ГОРОДУ) TotalOrderAmount,
Avg(сумма) OVER(PARTITION BY city) AvgOrderAmount,
Мин.(сумма) БОЛЕЕ(РАЗДЕЛ ПО ГОРОДУ) MinOrderAmount,
MAX(сумма) OVER(РАЗБИВКА ПО городам) MaxOrderAmount
FROM [SalesLT].[Orders] 

Использование предложения SQL PARTITION BY с функцией ROW_NUMBER()

Раньше мы получали агрегированные значения в окне с помощью предложения PARTITION BY. Предположим, что вместо итога нам требуется совокупный итог в разделе.

Нарастающий итог работает следующим образом.

Ряд Итого
1 Ранг 1+ 2
2 Ранг 2+3
3 Ранг 3+4

Ранг строки вычисляется с помощью функции ROW_NUMBER(). Давайте сначала воспользуемся этой функцией и просмотрим ранги строк.

  • Функция ROW_NUMBER() использует предложения OVER и PARTITION BY и сортирует результаты в порядке возрастания или убывания. Он начинает ранжировать строки с 1 в соответствии с порядком сортировки.
 ВЫБЕРИТЕ город КАК CustomerCity, CustomerName, сумма,
ROW_NUMBER() OVER(PARTITION BY city ORDER BY amount DESC) AS [Номер строки]
ОТ [SalesLT].[Заказы] 

Например, в городе [Алвар] строка с наибольшей суммой (25000,00) находится в строке 1. Как показано ниже, она ранжирует строки в окне, заданном предложением PARTITION BY. Например, у нас есть три разных города [Алвар], [Джайпур] и [Кота], и каждое окно (город) получает свои ряды рангов.

Для расчета нарастающего итога мы используем следующие аргументы.

  • ТЕКУЩАЯ СТРОКА: Указывает начальную и конечную точки в указанном диапазоне.
  • 1 следующий: Указывает количество строк (1), следующих за текущей строкой.
 ВЫБЕРИТЕ город КАК CustomerCity, CustomerName, сумма,
ROW_NUMBER() OVER(PARTITION BY city ORDER BY amount DESC) AS [Номер строки],
СУММА(сумма) БОЛЕЕ(РАЗДЕЛЕНИЕ ПО ГОРОДАМ ПО УСЛОВИЯМ ПО УИСШЕНИЮ СТРОК МЕЖДУ
ТЕКУЩАЯ СТРОКА И 1 СЛЕДУЮЩАЯ) AS CumulativeSUM
FROM [SalesLT]. [Orders] 

На следующем изображении показано, что вы получаете совокупный итог вместо общего итога в окне, заданном предложением PARTITION BY.

Если мы используем ROWS UNBOUNDED PRECEDING в предложении SQL PARTITION BY, он вычисляет совокупный итог следующим образом. Он использует текущие строки вместе со строками, имеющими самые высокие значения в указанном окне.

Ряд Итого
1 Ранг 1
2 Ранг 1+2
3 Ранг 1+2+3
 ВЫБЕРИТЕ город КАК CustomerCity, CustomerName, сумма,
ROW_NUMBER() OVER(PARTITION BY city ORDER BY amount DESC) AS [Номер строки],
СУММА(сумма) БОЛЕЕ(РАЗБИВКА ПО ГОРОДАМ ПО УСЛОВИЯМ ПО УИСЛАМ
ROWS UNBOUNDED PRECEDING) AS CumulativeSUM
ОТ [SalesLT].[Заказы] 

Сравнение предложения GROUP BY и SQL PARTITION BY

GROUP BY РАЗДЕЛ ПО
Возвращает одну строку на группу после вычисления совокупных значений.

Imacros | Все права защищены © 2021