Якщо ви вивчаєте або використовуєте SQL досить довго, ви могли чути про вікна або аналітичні функції, про те, наскільки вони важливі і як важливо їх розуміти.
У цій статті я опишу конкретні сценарії, де вам потрібно використовувати функції Windows і як їх застосовувати, щоб отримати бажаний результат.
Я розповім, зокрема, про такі функції Windows, перелічені нижче
- LAG()
- LEAD()
- ROW_NUMBER()
- RANK()
- DENSE_RANK()
- FIRST_VALUE()
- LAST_VALUE()
1.LAG()
Розглянемо сценарій, в якому у вас є таблиця, що містить щомісячну виручку від продажів, як показано нижче, і вам потрібно написати запит, який покаже щомісячну зміну виручки у відсотках за період з січня по грудень.
Ми можемо використовувати функцію LAG, щоб отримати всі записи з попереднього рядка, просто використовуючи цей синтаксис LAG:
LAG(НАЗВА_СТОВПЦЯ, N)
N вказує на те, на скільки рядків назад потрібно відступити
Інша частина функції вказує стовпець, за яким потрібно впорядкувати, ось як виглядає повний синтаксис.
LAG(REVENUE, 1) OVER (ORDER BY MONTH_ID) AS PREVIOUS_MONTH_REVENUE
У нашому випадку ми впорядковуємо за MONTH_ID за зростанням, оскільки MONTH_ID ідеально впорядковується за зростанням.
ОСЬ ПОВНИЙ СИНТАКСИС
SELECT
MONTH_ID, MONTH_NAME,
LAG(REVENUE, 1) OVER (ORDER BY MONTH_ID) AS PREVIOUS_MONTH_REVENUE,
REVENUE AS CURRENT_MONTH_REVENUE
FROM MONTHLY_REVENUE_DETAIL
Ось результат
Ви можете легко порівняти дохід від попереднього місяця з поточним і відстежити зростання/падіння доходу, а також відстежити відсоток зростання доходу від місяця до місяця, використовуючи формулу і синтаксис, наведені нижче.
FORMULA FOR CALCULATING REVENUE CHANGE
MONTHLY_REVENUE_CHANGE = CURRENT_MONTH_REVENUE - PREVIOUS_MONTH_REVENUE
MONTHLY_PERCENTAGE_REVENUE_CHANGE =
(CURRENT_MONTH_REVENUE - PREVIOUS_MONTH_REVENUE)/PREVIOUS_MONTH_REVENUE * 100
SELECT
MONTH_ID, MONTH_NAME,
LAG(REVENUE, 1) OVER (ORDER BY MONTH_ID) AS PREVIOUS_MONTH_REVENUE,
REVENUE,
FORMAT((REVENUE - LAG(REVENUE, 1) OVER (ORDER BY MONTH_ID))/
LAG(REVENUE, 1) OVER (ORDER BY MONTH_ID), 'P')
FROM MONTHLY_REVENUE_DETAILS
ОСЬ РЕЗУЛЬТАТ
Зауважте, що якщо ви хочете попрактикувати деякі з уроків, викладених у цій статті, ви можете скопіювати наведений нижче код, вставити його у ваше середовище MYSQL або SQL SERVER і запустити для створення таблиць, використаних у цій статті.
ДЛЯ КОРИСТУВАЧІВ MYSQL
CREATE DATABASE WINDOWS_FUNCTION_PRACTICE_MEDIUM_ARTICLE;
USE WINDOWS_FUNCTION_PRACTICE_MEDIUM_ARTICLE;
CREATE TABLE MONTHLY_REVENUE_DETAILS
(MONTH_ID INT,
MONTH_NAME TEXT,
REVENUE NUMERIC(10,2));
INSERT INTO MONTHLY_REVENUE_DETAILS
VALUES (1, 'JANUARY', 50500),
(2, 'FEBRUARY', 42500),
(3, 'MARCH', 65000),
(4, 'APRIL', 71000),
(5, 'MAY', 68000),
(6, 'JUNE', 59000),
(7, 'JULY', 81000),
(8, 'AUGUST', 71500),
(9, 'SPETEMBER', 64000),
(10, 'OCTOBER', 87000),
(11, 'NOVEMBER', 89000),
(12, 'DECEMBER', 125000);
CREATE TABLE TESLA_YEARLY_STOCK_PRICE_HISTORY(
YEAR INT,
REVENUE FLOAT);
INSERT INTO TESLA_YEARLY_STOCK_PRICE_HISTORY
VALUES (2009, 112000000),
(2010, 117000000),
(2011, 204000000),
(2012, 413000000),
(2013, 2013000000),
(2014, 3198000000),
(2015, 4046000000),
(2016, 7000000000),
(2017, 11759000000),
(2018, 21461000000),
(2019, 24578000000),
(2020, 31536000000),
(2021, 53823000000),
(2022, 81462000000),
(2023, 96773000000);
ДЛЯ КОРИСТУВАЧІВ SQL-СЕРВЕРІВ
CREATE DATABASE WINDOWS_FUNCTION_PRACTICE_MEDIUM_ARTICLE;
GO
USE WINDOWS_FUNCTION_PRACTICE_MEDIUM_ARTICLE;
GO
CREATE TABLE MONTHLY_REVENUE_DETAILS
(MONTH_ID INT,
MONTH_NAME TEXT,
REVENUE NUMERIC(10,2));
GO
INSERT INTO MONTHLY_REVENUE_DETAILS
VALUES (1, 'JANUARY', 50500),
(2, 'FEBRUARY', 42500),
(3, 'MARCH', 65000),
(4, 'APRIL', 71000),
(5, 'MAY', 68000),
(6, 'JUNE', 59000),
(7, 'JULY', 81000),
(8, 'AUGUST', 71500),
(9, 'SPETEMBER', 64000),
(10, 'OCTOBER', 87000),
(11, 'NOVEMBER', 89000),
(12, 'DECEMBER', 125000);
GO
CREATE TABLE TESLA_YEARLY_STOCK_PRICE_HISTORY(
YEAR INT,
REVENUE FLOAT);
GO
INSERT INTO TESLA_YEARLY_STOCK_PRICE_HISTORY
VALUES (2009, 112000000),
(2010, 117000000),
(2011, 204000000),
(2012, 413000000),
(2013, 2013000000),
(2014, 3198000000),
(2015, 4046000000),
(2016, 7000000000),
(2017, 11759000000),
(2018, 21461000000),
(2019, 24578000000),
(2020, 31536000000),
(2021, 53823000000),
(2022, 81462000000),
(2023, 96773000000);
GO
Перш ніж ми продовжимо, пам’ятайте, що ви можете зв’язатися зі мною в соціальній мережі, натиснувши на це посилання:www.linkedin.com/in/ifeanyi-okoye-a857911ba
2. LEAD() FUNCTION
LEAD() – це просто протилежність LAG(), оскільки вона повертає значення у новому рядку, а не значення у попередньому рядку.
LEAD() СИНТАКСИС
LEAD(НАЗВА_СТОВПЧИКА, N)
LEAD(REVENUE, 1) OVER (ORDER BY MONTH_ID) AS NEXT_MONTH_REVENUE
SELECT
MONTH_ID, MONTH_NAME, REVENUE,
LEAD(REVENUE, 1) OVER (ORDER BY MONTH_ID) AS NEXT_MONTH_REVENUE
FROM MONTHLY_REVENUE_DETAILS
ЯК І ФУНКЦІЮ LAG(), МИ ТАКОЖ МОЖЕМО ВИКОРИСТОВУВАТИ ЦЮ ФУНКЦІЮ ДЛЯ ОБЧИСЛЕННЯ ВІДСОТКОВОГО ЗРОСТАННЯ ДОХОДУ, ВИКОРИСТОВУЮЧИ НАВЕДЕНИЙ НИЖЧЕ СИНТАКСИС.
SELECT
MONTH_ID, MONTH_NAME,
REVENUE,
LEAD(REVENUE, 1) OVER (ORDER BY MONTH_ID) AS NEXT_MONTH_REVENUE,
FORMAT((LEAD(REVENUE, 1) OVER (ORDER BY MONTH_ID) - REVENUE)
/REVENUE, 'P')
FROM MONTHLY_REVENUE_DETAILS
Якщо ви уважно подивитеся на цю таблицю, то помітите, що в кінці є нульове значення як для стовпчика NEXT_MONTH_REVENUE, так і для REVENUE_CHANGE_PERCENT, це просто тому, що немає наступного рядка для генерування, і тому надається нульове значення, те ж саме стосується LAG(), перший рядок також має нульове значення.
3. ROW_NUMBER(), RANK(), DENSE_RANK()
Ці 3 функції дуже схожі, але мають невеликі відмінності.
Розглянемо цю таблицю нижче.
Зверніть увагу на повторення 500 (500 зустрічається в цій таблиці 3 рази),
і (600 зустрічається в цій таблиці двічі).
Тепер давайте застосуємо всі ці функції до цієї таблиці, використовуючи наведений нижче синтаксис.
SELECT *,
ROW_NUMBER() OVER (ORDER BY NUMBER) AS ROW_NUMBER,
RANK() OVER (ORDER BY NUMBER) AS RANK,
DENSE_RANK() OVER (ORDER BY NUMBER) AS DENSE_RANK
FROM LIST_OF_NUMBERS
І ось результат.
ROW_NUMBER()
: Ця функція присвоює новий номер для кожного рядка незалежно від того, чи є номер однаковим чи ні.
RANK()
: Rank присвоює однаковий номер, якщо номер однаковий, але пропускає номери залежно від кількості рядків, яким він присвоїв однаковий номер. У наведеному вище прикладі 500 зустрічається 3 рази, тому у 4-му рядку, де вперше зустрічається 600, 600 буде присвоєно номер 4
DENSE_RANK()
: Щільний ранг також присвоює однаковий номер кожному рядку, оскільки їхні номери однакові, але не пропускає рядки, на відміну від ранжирування.
СЦЕНАРІЙ
В якому сценарії ми можемо використовувати ці функції.
Розглянемо таблицю нижче
Зверніть увагу на перші два стовпчики, кожна назва додатку має категорію, а кожна категорія має декілька назв додатків. Як бачимо вище, у категорії ПОСЛУГИ є додатки «нявкання», «метамаска» та «неторговий».
Тепер вам потрібно знайти 2 найкращі додатки з найбільшим доходом у кожній категорії. Ось як це можна зробити за допомогою функції Windows.
Спочатку ви пишете запит, який ранжує кожну програму на основі її щоденного доходу і присвоює номер кожному рядку, використовуючи наведений нижче синтаксис.
SELECT Category,
Module as App_Name,
FORMAT(ROUND(SUM(Daily_Revenue), -3), 'C', 'US-US')
AS REVENUE_PER_MODULE,
ROW_NUMBER() OVER (PARTITION BY Category
ORDER BY SUM(Daily_Revenue) DESC) AS RN
FROM [dbo].[DW_DEFI_REVENUE_TABLE]
GROUP BY Category, Module
Результат
Кожне ім’я_застосунку ранжується на основі доходу для кожної категорії, тепер ми можемо отримати 2 найкращі застосунки з найбільшим доходом у кожній категорії, додавши речення where до цього оператора за допомогою підзапиту.
Синтаксис нижче.
SELECT *
FROM (
SELECT Category,
Module as App_Name,
FORMAT(ROUND(SUM(Daily_Revenue), -3), 'C', 'US-US') AS REVENUE_PER_MODULE,
ROW_NUMBER() OVER (PARTITION BY Category
ORDER BY SUM(Daily_Revenue) DESC) AS RN
FROM [dbo].[DW_DEFI_REVENUE_TABLE]
GROUP BY Category, Module
) SUB_SUB
WHERE RN <= 2
Результат
Ми отримали лише 2 найкращі програми в кожній категорії за критерієм «Дохід».
Функції Windows також стають у нагоді при спробі виявити повторювані рядки, значення у наборі даних.
Розглянемо таблицю нижче
Ми бачимо, що в цій таблиці присутній дублікат запису, ось як ми можемо позбутися його за допомогою функції вікон.
- Використовуючи Partition з
ROW_NUMBER()
, ми можемо виявити повторювані дані
СИНТАКСИС НИЖЧЕ
SELECT SALES_ID, ORDER_SUM,
ROW_NUMBER() OVER
(PARTITION BY SALES_ID, ORDER_SUM ORDER BY SALES_ID, ORDER_SUM)
FROM SALES_ORDERS
Дубльований запис легко ідентифікувати за допомогою числа 2, оскільки ROW_NUMBER() бачить, що саме такий рядок вже існує в таблиці.
Повторювані дані можна ідентифікувати за допомогою запиту, наведеного нижче.
WITH DIRTY_TABLE AS
(
SELECT SALES_ID, ORDER_SUM,
ROW_NUMBER() OVER
(PARTITION BY SALES_ID, ORDER_SUM ORDER BY SALES_ID, ORDER_SUM)
AS RN
FROM SALES_ORDERS
)
SELECT *
FROM DIRTY_TABLE
WHERE RN > 1
РЕЗУЛЬТАТ
Нарешті, ми можемо видалити ці дублікати даних за допомогою речення DELETE І WHERE.
SYNTAX
WITH DIRTY_TABLE AS
(
SELECT SALES_ID, ORDER_SUM,
ROW_NUMBER() OVER
(PARTITION BY SALES_ID, ORDER_SUM ORDER BY SALES_ID, ORDER_SUM)
AS RN
FROM SALES_ORDERS
)
DELETE
FROM DIRTY_TABLE
WHERE RN > 1
4. FIRST_VALUE() AND LAST_VALUE()
СЦЕНАРІЙ:
Ось таблиця, що показує загальний річний дохід Tesla з 2009 по 2023 рік
Вам потрібно обчислити зростання доходу Tesla з 2009 по 2023 рік і показати його у відсотках.
Ви можете легко отримати саме ПЕРШЕ ЗНАЧЕННЯ і ОСТАННЄ ЗНАЧЕННЯ на основі ієрархії з заданого набору даних за допомогою функцій FIRST_VALUE()
і LAST_VALUE()
.
СИНТАКСИС ФУНКЦІЇ FIRST_VALUE()
FIRST_VALUE(COLUMN_NAME) OVER
(ORDER BY HIERARCHY_COLUMN) AS FIRST_VALUE
СИНТАКСИС LAST_VALUE()
LAST_VALUE(COLUMN_NAME) OVER
(ORDER BY HIERARCHY_COLUMN) AS LAST_VALUE
Спочатку давайте порахуємо зміну доходів між 2009 та 2023 роками.
СИНТАКСИС НИЖЧЕ
SELECT TOP 1
YEAR,
REVENUE,
FIRST_VALUE(REVENUE) OVER
(ORDER BY YEAR) AS FIRST_YEAR_REVENUE,
LAST_VALUE(REVENUE) OVER
(ORDER BY YEAR DESC) AS MOST_RECENT_YEAR_REVENUE
FROM TESLA_YEARLY_STOCK_PRICE_HISTORY
Зверніть увагу, що я використовую TOP 1, тому що мені потрібні тільки верхні значення в цьому запиті, для користувачів SQL SERVER ви можете використовувати TOP 1, для користувачів MYSQL ви можете використовувати LIMIT 1, як показано нижче.
SELECT YEAR,
REVENUE,
FIRST_VALUE(REVENUE) OVER
(ORDER BY YEAR) AS FIRST_YEAR_REVENUE,
LAST_VALUE(REVENUE) OVER
(ORDER BY YEAR DESC) AS MOST_RECENT_YEAR_REVENUE
FROM TESLA_YEARLY_STOCK_PRICE_HISTORY
LIMIT 1
Далі ви можете розрахувати відсоткову зміну доходу Tesla від першого до останнього року, використовуючи наведений нижче синтаксис.
HISTORICAL REVENUE CHANGE FORMULA
REVENUE CHANGE = ((MOST_REVENT_REVENUE - FIRST_YEAR_REVENUE)
/FIRST_YEAR_REVENUE) * 100
ДЛЯ КОРИСТУВАЧІВ СЕРВЕРА SQL (ВИКОРИСТОВУЙТЕ АБО СКОПІЮЙТЕ ЗАПИТ НИЖЧЕ)
SELECT TOP 1
YEAR,
FORMAT(FIRST_VALUE(REVENUE) OVER
(ORDER BY YEAR), 'C', 'US-US') AS FIRST_YEAR_REVENUE,
FORMAT(LAST_VALUE(REVENUE) OVER
(ORDER BY YEAR DESC), 'C', 'US-US') AS MOST_RECENT_YEAR_REVENUE,
FORMAT((LAST_VALUE(REVENUE) OVER
(ORDER BY YEAR DESC) - FIRST_VALUE(REVENUE) OVER
(ORDER BY YEAR))/FIRST_VALUE(REVENUE) OVER
(ORDER BY YEAR), 'P') AS PRICE_CHANGE_SINCE_FIRST_YEAR
FROM TESLA_YEARLY_STOCK_PRICE_HISTORY
РЕЗУЛЬТАТ
Виручка Tesla зросла на 86 304% з 2009 по 2023 рік.
ДЛЯ КОРИСТУВАЧІВ MYSQL (ВИКОРИСТОВУЙТЕ АБО СКОПІЮЙТЕ ЗАПИТ НИЖЧЕ), ЩОБ ОТРИМАТИ ТАКИЙ САМИЙ РЕЗУЛЬТАТ
SELECT
YEAR,
FORMAT(FIRST_VALUE(REVENUE) OVER
(ORDER BY YEAR), 'C', 'US-US') AS FIRST_YEAR_REVENUE,
FORMAT(LAST_VALUE(REVENUE) OVER
(ORDER BY YEAR DESC), 'C', 'US-US') AS MOST_RECENT_YEAR_REVENUE,
((LAST_VALUE(REVENUE) OVER
(ORDER BY YEAR DESC) - FIRST_VALUE(REVENUE) OVER
(ORDER BY YEAR))/FIRST_VALUE(REVENUE) OVER
(ORDER BY YEAR)) * 100 AS PRICE_CHANGE_SINCE_FIRST_YEAR
FROM TESLA_YEARLY_STOCK_PRICE_HISTORY
LIMIT 1
Аналітичні функції Windows надзвичайно ефективні для комплексного аналізу даних, просто потрібно знати, коли і як їх використовувати.
Сподіваємось, ця стаття допомогла вам у цьому.
Не соромтеся додавати свої коментарі в розділі коментарів, дякую за прочитання.
Пам’ятайте, що ви можете зв’язатися зі мною через LINKED IN за цим посиланням:
www.linkedin.com/in/ifeanyi-okoye-a857911ba
🚀Долучайтесь до нашої спільноти Telegram:
🚀Долучайтесь до нашої спільноти FaceBook: