0Pricing
SQL Academy · Урок

Звёздная и снежинка-схема

Моделируйте данные для быстрой аналитики

«Звёздная и снежинка-схема» — бесплатный урок SQL Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения SQL Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс SQL Academy содержит 4 уроков всего.

Что такое схема хранилища данных

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

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

Таблицы фактов и таблицы измерений

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

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

CREATE TABLE fact_sales (
  sale_id      SERIAL PRIMARY KEY,
  date_key     INT NOT NULL,
  product_key  INT NOT NULL,
  customer_key INT NOT NULL,
  store_key    INT NOT NULL,
  quantity     INT NOT NULL,
  revenue      NUMERIC(12, 2) NOT NULL
);

CREATE TABLE dim_product (
  product_key  SERIAL PRIMARY KEY,
  product_name VARCHAR(200),
  category     VARCHAR(100),
  brand        VARCHAR(100),
  unit_price   NUMERIC(10, 2)
);

Звездная схема

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

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

-- Star schema: all product info in one flat dimension table
CREATE TABLE dim_product (
  product_key    SERIAL PRIMARY KEY,
  product_name   VARCHAR(200),
  category_name  VARCHAR(100),   -- denormalized
  subcategory    VARCHAR(100),   -- denormalized
  brand_name     VARCHAR(100),   -- denormalized
  brand_country  VARCHAR(100),   -- denormalized
  unit_price     NUMERIC(10, 2)
);

CREATE TABLE dim_date (
  date_key   INT PRIMARY KEY,   -- e.g. 20240315
  full_date  DATE,
  year       INT,
  quarter    INT,
  month      INT,
  month_name VARCHAR(20),
  week       INT,
  day_of_week VARCHAR(10)
);

Запрос к звездной схеме

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

Именно поэтому звездные схемы обеспечивают быстрые аналитические запросы: граф соединений неглубокий.

SELECT
  d.year,
  d.quarter,
  p.category_name,
  SUM(f.revenue)   AS total_revenue,
  SUM(f.quantity)  AS units_sold
FROM fact_sales f
JOIN dim_date    d ON d.date_key    = f.date_key
JOIN dim_product p ON p.product_key = f.product_key
WHERE d.year = 2024
GROUP BY d.year, d.quarter, p.category_name
ORDER BY d.quarter, total_revenue DESC;

Схема «снежинка»

Схема снежинка дополнительно нормализует таблицы измерений, разделяя их на подизмерения. Например, вместо хранения category_name и brand_name в таблице dim_product создаются отдельные таблицы dim_category и dim_brand.

Получившаяся диаграмма напоминает снежинку — ветви связанных таблиц.

-- Snowflake schema: product dimension is normalized
CREATE TABLE dim_brand (
  brand_key     SERIAL PRIMARY KEY,
  brand_name    VARCHAR(100),
  brand_country VARCHAR(100)
);

CREATE TABLE dim_category (
  category_key   SERIAL PRIMARY KEY,
  category_name  VARCHAR(100),
  subcategory    VARCHAR(100)
);

CREATE TABLE dim_product (
  product_key  SERIAL PRIMARY KEY,
  product_name VARCHAR(200),
  category_key INT REFERENCES dim_category(category_key),
  brand_key    INT REFERENCES dim_brand(brand_key),
  unit_price   NUMERIC(10, 2)
);

Запрос к схеме «снежинка»

Для запроса к схеме «снежинка» требуется больше соединений, чтобы восстановить данные измерений, разделённые между таблицами. Оптимизатор запросов должен пройти дополнительные уровни, что может увеличить задержку по сравнению со звёздной схемой.

Однако нормализованные измерения занимают меньше места и согласованы между собой: изменение названия бренда в одной строке dim_brand автоматически применяется во всех местах.

SELECT
  d.year,
  c.category_name,
  b.brand_name,
  SUM(f.revenue) AS total_revenue
FROM fact_sales    f
JOIN dim_date      d ON d.date_key    = f.date_key
JOIN dim_product   p ON p.product_key = f.product_key
JOIN dim_category  c ON c.category_key = p.category_key
JOIN dim_brand     b ON b.brand_key    = p.brand_key
WHERE d.year = 2024
GROUP BY d.year, c.category_name, b.brand_name
ORDER BY total_revenue DESC;

Суррогатные ключи и естественные ключи

Таблицы измерений обычно используют суррогатный ключ — целое число, сгенерированное хранилищем (например, SERIAL), — а не естественный ключ из исходной системы.

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

-- Surrogate key (product_key) vs natural key (sku)
INSERT INTO dim_product (product_name, category_key, brand_key, unit_price)
VALUES ('Wireless Headphones', 3, 7, 89.99);
-- product_key is assigned by SERIAL -- the natural key (SKU) lives elsewhere

-- Natural key would be:
-- INSERT INTO dim_product (sku, product_name, ...)
-- VALUES ('WH-1000XM5', 'Wireless Headphones', ...);
-- Risky: SKU can be reused or reassigned by the source system

Измерение даты

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

-- Populate dim_date for one year using generate_series
INSERT INTO dim_date (date_key, full_date, year, quarter, month, month_name, week, day_of_week)
SELECT
  TO_CHAR(d, 'YYYYMMDD')::INT  AS date_key,
  d                             AS full_date,
  EXTRACT(YEAR    FROM d)::INT  AS year,
  EXTRACT(QUARTER FROM d)::INT  AS quarter,
  EXTRACT(MONTH   FROM d)::INT  AS month,
  TO_CHAR(d, 'Month')           AS month_name,
  EXTRACT(WEEK    FROM d)::INT  AS week,
  TO_CHAR(d, 'Day')             AS day_of_week
FROM generate_series('2024-01-01'::DATE, '2024-12-31'::DATE, '1 day') AS d;

Медленно изменяющиеся измерения (SCD типа 2)

Что происходит, когда клиент переезжает в другой город или товар меняет категорию? Необходимо отслеживать историю. SCD типа 2 добавляет новую строку измерения при каждом изменении, закрывая предыдущую строку датой окончания. Строка таблицы фактов по-прежнему ссылается на старый ключ измерения, сохраняя историческую точность.

-- SCD Type 2 customer dimension
CREATE TABLE dim_customer (
  customer_key  SERIAL PRIMARY KEY,
  customer_id   INT NOT NULL,
  customer_name VARCHAR(200),
  city          VARCHAR(100),
  country       VARCHAR(100),
  valid_from    DATE NOT NULL,
  valid_to      DATE,
  is_current    BOOLEAN DEFAULT TRUE
);

-- When a customer moves, close old row and insert new one:
UPDATE dim_customer
   SET valid_to = CURRENT_DATE - 1, is_current = FALSE
 WHERE customer_id = 42 AND is_current = TRUE;

INSERT INTO dim_customer (customer_id, customer_name, city, country, valid_from, is_current)
VALUES (42, 'Alice Muller', 'Berlin', 'Germany', CURRENT_DATE, TRUE);

Звёздная схема и схема «снежинка» — компромиссы

Ни одна схема не лучше во всех случаях. Выбирайте схему с учётом своих приоритетов:

  • Звёздная схема — меньше соединений, более быстрые запросы, более простой ETL, более высокие затраты на хранение. Лучше всего подходит для инструментов аналитики с преобладанием чтения (Tableau, Power BI).
  • Схема «снежинка» — нормализованные измерения, меньше избыточности, более удобное обновление измерений, но больше соединений. Лучше подходит, когда измерения велики или используются совместно несколькими таблицами фактов.
-- Checking how much storage the denormalized category column costs
-- in a large dim_product (star schema) vs a separate dim_category (snowflake)
SELECT
  COUNT(*)                               AS total_products,
  COUNT(DISTINCT category_name)          AS unique_categories,
  pg_size_pretty(
    SUM(pg_column_size(category_name))
  )                                      AS category_storage
FROM dim_product;

Схема «созвездие» (созвездие фактов)

Когда в хранилище есть несколько таблиц фактов, использующих общие таблицы измерений, результат называют схемой «созвездие» (или созвездием фактов). Например, в хранилище розничных данных могут быть отдельные таблицы фактов для продаж и возвратов, обе ссылающиеся на одни и те же dim_product и dim_date.

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

CREATE TABLE fact_returns (
  return_id     SERIAL PRIMARY KEY,
  date_key      INT NOT NULL REFERENCES dim_date(date_key),
  product_key   INT NOT NULL REFERENCES dim_product(product_key),
  customer_key  INT NOT NULL,
  quantity      INT NOT NULL,
  refund_amount NUMERIC(12, 2) NOT NULL
);

-- Cross-fact query: net revenue = sales - refunds
SELECT
  d.year,
  d.month,
  SUM(s.revenue)       AS gross_revenue,
  SUM(r.refund_amount) AS total_refunds,
  SUM(s.revenue) - COALESCE(SUM(r.refund_amount), 0) AS net_revenue
FROM dim_date d
LEFT JOIN fact_sales   s ON s.date_key = d.date_key
LEFT JOIN fact_returns r ON r.date_key = d.date_key
WHERE d.year = 2024
GROUP BY d.year, d.month
ORDER BY d.month;

Звёздная схема и схема «снежинка»

Проверьте свои знания о звёздных схемах и схемах «снежинка».

Итоги урока

В этом уроке вы изучили два базовых шаблона проектирования хранилищ данных:

  • Звёздная схема — центральная таблица фактов, окружённая плоскими денормализованными таблицами измерений. Меньше соединений, более быстрые запросы, немного больше места для хранения.
  • Схема «снежинка» — таблицы измерений дополнительно нормализованы в подизмерения. Меньше избыточности, более удобные обновления, но требуется больше соединений.
  • Таблицы фактов хранят измеримые события; таблицы измерений предоставляют контекст (кто, что, когда, где).
  • Суррогатные ключи сохраняют историческую точность и отделяют хранилище от изменений в исходной системе.
  • SCD типа 2 отслеживает историю измерений, добавляя новые строки с датами действия вместо перезаписи старых.
  • Когда несколько таблиц фактов используют общие измерения, получается схема «созвездие» (созвездие фактов).

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

Часто задаваемые вопросы

Урок «Звёздная и снежинка-схема» бесплатный?

Да — полный текст урока «Звёздная и снежинка-схема» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс SQL Academy, подпишись на CoddyKit PRO. Курс SQL Academy содержит 4 уроков всего.

Чему я научусь в уроке «Звёздная и снежинка-схема»?

Моделируйте данные для быстрой аналитики Ты практикуешь SQL Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать SQL Academy?

Предыдущий опыт не требуется. SQL Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.

Сколько времени занимает урок «Звёздная и снежинка-схема»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке SQL Academy?

Да. Каждый урок SQL Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. OLTP и OLAP
  2. Таблицы фактов и измерений
  3. Звёздная и снежинка-схема
  4. Написание аналитических запросов
← Назад к SQL Academy