Продвинутая аналитика на SQL

Теория: Концепция витрины

В этом уроке мы познакомимся с понятиями баз и витрин данных компании и их различиями. Витрины — важная тема для аналитика данных, так как он работает именно с ними.

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

Базы данных систем

Представим, что мы ведем заметки о своей жизни и работе в разных местах. Для учета доходов и расходов мы используем Google-таблицы или специальные приложения. Трекинг рабочих задач производим в Trello. А в Notion записываем мысли о событиях из жизни и ведем дневник. Каждый из этих инструментов удобен для определенных задач.

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

Обычно базы данных подсистем компании хранятся в нормализованном виде. Покажем, как это выглядит на примере.

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

Purchases

datefull_nameshop_addressproduct_namepricediscountdiscounted_price
22.12.2022Попов Константин ФедоровичЛенинградский проспект 12, МоскваСтиральная машина Electrolux284990.125649
22.12.2022Гречкина Анастасия Антоновнаул Нансена 83, Ростов-на-ДонуБеспроводные наушники JBL с микрофоном49990.054749
24.12.2022Алексеев Алексей МихайловичЛиговский проспект 30, Санкт-ПетербургИгровая мышь Acer10990.071022
.....................

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

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

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

Чтобы не столкнуться с этой проблемой, базы данных таких систем нормализуют — разносят разные объекты по разным таблицам. В таком случае предстоит изменять минимальное количество строк.

Так выглядят нормализованные таблицы:

Shops

idshop_address
274Ленинградский проспект 12, Москва
528ул Нансена 83, Ростов-на-Дону
182Лиговский проспект 30, Санкт-Петербург
......

Customers

idfull_name
24681982Попов Константин Федорович
47560098Гречкина Анастасия Антоновна
98738112Алексеев Алексей Михайлович
......

Products

idproduct_nameprice
10928473Стиральная машина Electrolux28499.99
20483739Беспроводные наушники JBL с микрофоном4999.0
73648393Игровая мышь Acer1099.0
.........

Discounts

iddiscount
7765340.1
7654320.05
6928370.07
......

CustomersDiscounts

idcustomer_iddiscount_id
336748824681982776534
333748247560098765432
287640998738112692837
.........

Purchases

iddatecustomer_idproduct_idshop_id
188872637922.12.20222468198210928473274
188872638022.12.20224756009820483739528
188872638124.12.20229873811273648393182
...............

Датасет доступен на DB Fiddle по этой ссылке.

Здесь представлены следующие таблицы:

  • Shops — информация о магазинах
  • Customers — информация о клиентах
  • Products — информация о товарах
  • Discounts — информация о скидках
  • CustomersDiscounts — таблица, которая связывает клиентов и их персональные скидки
  • Purchases — таблица покупок

У каждой из этих таблиц есть поле ID — это уникальный ключ. Также одна таблица содержит в себе информацию только об одной сущности, например: Shops — о магазинах, Customers — о клиентах.

Каждый объект, например, магазин, клиент, представлены одной строкой без дублирования. При этом таблица Purchases может содержать одинаковые ID клиентов, товаров или магазинов, так как клиенты покупают товары в разных магазинах в разное время или одни и те же товары.

Если заменить адрес магазина, мы затронем одну строчку в таблице с магазинами. Остальные таблицы останутся без изменений.

CustomersDiscounts — это отдельный вид таблиц. Они содержат в себе только свой ID и ID других таблиц, что позволяет связать эти таблицы. Покупателей и их индивидуальные скидки разносят по разным таблицам.

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

Такие базы данных называются OLTP или Online Transaction Processing. Online — потому, что базы данных обновляются в реальном времени. Транзакции идут большим потоком, поэтому, чтобы достичь минимального времени отклика, важно вносить изменения в малое количество строк.

Витрины данных

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

Пример витрины данных — это таблица Purchases. Посмотрим на нее еще раз:

Purchases

datefull_nameshop_addressproduct_namepricediscountdiscounted_price
22.12.2022Попов Константин ФедоровичЛенинградский проспект 12, МоскваСтиральная машина Electrolux25649.9910.125649
22.12.2022Гречкина Анастасия Антоновнаул Нансена 83, Ростов-на-ДонуБеспроводные наушники JBL с микрофоном49990.054749.05
24.12.2022Алексеев Алексей МихайловичЛиговский проспект 30, Санкт-ПетербургИгровая мышь Acer10990.071022.07
.....................

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

Напомним, что обычно в компаниях базы данных для каждого отдела разные. Аналитики обычно не анализируют все данные сразу, поэтому витрины — это срез во всех данных компании. Их еще называют Data Mart.

Представим общую схему потока данных между базами разных отделов и витринами:

OLTP_DataMart

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

Витрины данных могут быть базами данных или таблицами в Google Sheets, это не меняет их сути.

Теперь разберемся, как работать с витриной. Для этого создадим ее.

Создаем витрину

Откроем базу данных Purchases. Мы увидим в ней таблицы, которые были в примерах урока:

  • Customers
  • CustomersDiscounts
  • Discounts
  • Products
  • Purchases
  • Shops

Посмотрим на таблицу Purchases:

purchases.purchases

iddatecustomer_idproduct_idshop_id
188872637922.12.20222468198210928473274
188872638022.12.20224756009820483739528
188872638124.12.20229873811273648393182
188872638225.12.20229873811222657781182
188872638325.12.20221200097666379812274
188872638426.12.20226342887117728904280
188872638526.12.20226276541333801167182
188872638626.12.20229987162226659810274
188872638727.12.20223200965481176602182
188872638827.12.20228977412643287163280
188872638927.12.20221099877699842651274
188872639028.12.20227225411316673098274
188872639128.12.20222553675477649207528

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

  • date — дата покупки
  • full_name — ФИО покупателя
  • shop_address — адрес магазина
  • product_name — наименование товара
  • price — исходная цена товара в рублях
  • discount — скидка в долях от единицы
  • discounted_price — цена товара после индивидуальной скидки покупателю

Таблица Purchases будет основной связующей с другими таблицами. Друг с другом связаны только таблицы customers и discounts через промежуточную таблицу customersdiscounts.

Чтобы создать витрину, нужно объединить нормализованные таблицы джойнами. В нашем случае в таблицах нет NULL-значений, поэтому мы можем использовать любой вид джойнов, например, INNER JOIN. INNER JOIN — это разновидность джойна, которая связывает таблицы по ненулевым ключам. Если ключ какой-то строки имеет значение NULL, эта строка не попадает в итоговую таблицу.

Если в ключах есть NULL-значения, а мы все равно хотим включить эти строки в итоговую таблицу, мы воспользуемся LEFT JOIN, RIGHT JOIN или FULL OUTER JOIN. В таком случае мы выберем джойн исходя из того, включать ли нулевые ключи только левой таблицы, правой или обеих.

Теперь нужно понять, откуда взять нужные нам поля. Например, date — это дата покупки, поэтому возьмем ее из таблицы Purchases. А full_name есть в таблице customers. Попробуем составить селект, чтобы получить два этих поля:

SELECT
    date,
    full_name
FROM purchases
INNER JOIN customers
    ON purchases.customer_id = customers.id;

В customers id покупателя называется id, так как эта таблица про покупателей, и id покупателя в ней — это уникальный ключ. А таблица Purchases о покупках. Покупатели в ней могут повторяться, поэтому поле называется customer_id.

shop_address — это адрес магазина, то есть поле address в таблице Shops. Добавим джойн с этой таблицей:

SELECT
    date,
    full_name,
    address AS shop_address
FROM purchases
INNER JOIN customers
    ON purchases.customer_id = customers.id
INNER JOIN shops
    ON purchases.shop_id = shops.id;

product_name и price мы возьмем из таблицы Products:

SELECT
    date,
    full_name,
    address AS shop_address,
    product_name,
    price
FROM purchases
INNER JOIN customers
    ON purchases.customer_id = customers.id
INNER JOIN shops
    ON purchases.shop_id = shops.id
INNER JOIN products
    ON purchases.product_id = products.id;

Остались поля discount и discounted_price. Мы помним, что таблицы Discount и Customers связаны через CustomersDiscounts. Поэтому мы объединим скидки с покупателями через дополнительную таблицу. В итоге посчитаем discounted_price по следующей формуле:

discounted_price = price - price * discount = price * (1 - discount)

Итоговый запрос будет выглядеть так:

SELECT
    date,
    full_name,
    address AS shop_address,
    product_name,
    price,
    discount,
    ((1 - discount) * price) AS discounted_price
FROM purchases
INNER JOIN customers
    ON purchases.customer_id = customers.id
INNER JOIN shops
    ON purchases.shop_id = shops.id
INNER JOIN products
    ON purchases.product_id = products.id
INNER JOIN customersdiscounts
    ON customers.id = customersdiscounts.customer_id
INNER JOIN discounts
    ON customersdiscounts.discount_id = discounts.id;

data_mart

datefull_nameshop_addressproduct_namepricediscountdiscounted_price
22.12.2022Попов Константин ФедоровичЛенинградский проспект 12, МоскваСтиральная машина Electrolux284990.125649
22.12.2022Гречкина Анастасия Антоновнаул Нансена 83, Ростов-на-ДонуБеспроводные наушники JBL с микрофоном49990.054749
.....................

Ссылка на таблицы

Работаем с витриной

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

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

CREATE TABLE purchases_data_mart AS

SELECT
    date,
    full_name,
    address AS shop_address,
    product_name,
    price,
    discount,
    ((1 - discount) * price) AS discounted_price
FROM purchases
INNER JOIN customers
    ON purchases.customer_id = customers.id
INNER JOIN shops
    ON purchases.shop_id = shops.id
INNER JOIN products
    ON purchases.product_id = products.id
INNER JOIN customersdiscounts
    ON customers.id = customersdiscounts.customer_id
INNER JOIN discounts
    ON customersdiscounts.discount_id = discounts.id;

В запросе выше мы объединили все таблицы в одну и записали нашу витрину в новую таблицу.

Теперь мы можем составить запрос к таблице витрины:

SELECT
    shop_address,
    SUM(price) AS total_earnings
FROM purchases_data_mart
GROUP BY shop_address;

Итоговая таблица выглядит так:

purchases.purchasesDataMart

shop_addresstotal_earnings
Ленинградский проспект 12, Москва143095.99
Лиговский проспект 30, Санкт-Петербург56187.0
Ломоносовский проспект 23, Москва9640.0
ул Нансена 83, Ростов-на-Дону11898.0

Ссылка на таблицу

В этой таблице мы видим суммарную выручку по каждому из магазинов в колонке total_earnings.

Мы создали витрину данных и записали ее в таблицу purchasesDataMart, а также создали свой первый отчет по витрине.

Выводы

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

Мы научились создавать витрину. Чтобы превратить таблицы в базе в витрину, нужно объединить информативные поля из разных таблиц по ключам. Главный ключ в каждой из таблиц — это уникальное поле. Обычно он называется id.

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

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

Рекомендуемые программы

Завершено

0 / 10