Моделювання даних у Power BI: зв'язки та схема «зірка»
Ви завантажили кілька таблиць у Power BI, побудували візуали — і раптом цифри «поїхали»: сума продажів дублюється, зріз по місту не фільтрує графік, а міри повертають дивні значення. У 9 випадках із 10 причина не в DAX і не у візуалах, а в моделі даних: таблиці пов’язані неправильно або не пов’язані взагалі.

Це урок 7 безкоштовного курсу аналітики даних. У ньому ви розберетеся, що таке таблиці фактів і вимірів, як працюють зв’язки та кардинальність у Power BI, чому схема «зірка» — стандарт індустрії, і як створити календарну таблицю. Всі приклади — на знайомій демо-базі магазину: orders, customers, products, order_items.
Після цього уроку ваші звіти стануть передбачуваними: фільтри працюватимуть як очікується, а міри у наступному уроці про DAX писатимуться у 2–3 рядки замість заплутаних формул.
Навіщо взагалі потрібна модель даних
Модель даних — це набір таблиць у Power BI та зв’язків між ними. Саме зв’язки визначають, як фільтр з однієї таблиці впливає на іншу. Коли ви клікаєте на місто «Київ» у зрізі, Power BI «проходить» по зв’язку від customers до orders і залишає тільки замовлення київських клієнтів.
Можна піти іншим шляхом — злити все в одну широку таблицю ще в SQL або Power Query. Для простого звіту це спрацює, але з ростом даних ви отримаєте дублікати значень, роздутий файл і негнучкі розрахунки. Правильна модель вирішує три задачі:
- Коректність: агрегати не дублюються, фільтри працюють передбачувано.
- Швидкість: рушій VertiPaq стискає вузькі таблиці-виміри значно ефективніше, ніж одну широку таблицю.
- Простий DAX: у добре спроєктованій моделі більшість мір — це один SUM або COUNT.
Таблиці фактів і вимірів
В основі моделювання лежить поділ таблиць на два типи. Таблиця фактів зберігає події бізнесу — те, що сталося і що можна порахувати: замовлення, платежі, відвантаження. Таблиця вимірів описує контекст цих подій: хто купив, що купив, коли і де.
Розкладемо нашу демо-базу магазину за цією логікою:
| Таблиця | Тип | Що зберігає | Типовий розмір |
|---|---|---|---|
| order_items | Факт | Рядки замовлень: order_id, product_id, qty | Мільйони рядків |
| orders | Факт | Замовлення: customer_id, order_date, amount, status | Сотні тисяч рядків |
| customers | Вимір | Клієнти: name, city, created_at | Тисячі рядків |
| products | Вимір | Товари: name, category, price | Сотні рядків |
| Календар | Вимір | Дати, місяці, квартали, роки | 365 рядків на рік |
Просте правило: у фактах — числа, які ви агрегуєте (SUM, COUNT, AVG), у вимірах — атрибути, за якими ви фільтруєте і групуєте (місто, категорія, місяць). Факти ростуть щодня, виміри — повільно.
Якщо ви готуєте дані на боці MySQL, таблицю фактів зручно збагатити ще до імпорту — наприклад, додати суму рядка замовлення:
SELECT
oi.order_id,
oi.product_id,
o.customer_id,
o.order_date,
oi.qty,
oi.qty * p.price AS line_amount
FROM order_items AS oi
JOIN orders AS o ON o.id = oi.order_id
JOIN products AS p ON p.id = oi.product_id
WHERE o.status = 'completed';Зверніть увагу: ми додали до факту тільки ключі (customer_id, product_id) і числа. Назви клієнтів і категорії товарів залишаються у вимірах — за ними Power BI дістанеться через зв’язки.
Зв’язки у Power BI: кардинальність і напрям фільтрації
Зв’язок у Power BI з’єднує стовпець однієї таблиці зі стовпцем іншої — як зовнішній ключ у SQL, тільки працює він для фільтрації, а не для JOIN у запиті. Створюються зв’язки у вкладці «Model view»: перетягніть поле з однієї таблиці на відповідне поле іншої, наприклад customers[id] на orders[customer_id].
Кардинальність зв’язків
Кардинальність описує, скільки рядків з одного боку зв’язку відповідає рядкам з іншого:
| Кардинальність | Приклад у нашій моделі | Коли використовувати |
|---|---|---|
| Один-до-багатьох (1:*) | customers[id] → orders[customer_id] | Основний тип: вимір → факт |
| Один-до-одного (1:1) | customers → окрема таблиця з деталями клієнтів | Рідко; частіше таблиці варто об’єднати |
| Багато-до-багатьох (*:*) | orders ↔ таблиця акцій, що діють на кілька товарів | Тільки свідомо; легко отримати неоднозначні результати |
У 95% випадків вам потрібен «один-до-багатьох»: на боці «один» — вимір з унікальним ключем (customers[id]), на боці «багато» — факт (orders[customer_id]). Якщо Power BI пропонує зв’язок «багато-до-багатьох», це майже завжди сигнал, що в «довіднику» є дублікати ключа — почистіть дані в Power Query, а не погоджуйтеся на *:*.
Напрям крос-фільтрації
Кожен зв’язок має напрям фільтрації. Single (односпрямований) — фільтр іде від виміру до факту: обрали категорію в products — відфільтрувалися рядки в order_items. Це стандарт і безпечний вибір за замовчуванням.
Both (двоспрямований) — фільтр працює в обидва боки: тоді вибір категорії товару відфільтрує ще й клієнтів, які купували цю категорію. Звучить зручно, але двоспрямовані зв’язки створюють неоднозначні шляхи фільтрації та вповільнюють модель. Вмикайте Both тільки тоді, коли точно розумієте навіщо; для типового сценарію «зріз має показувати лише релевантні значення» краще підійде міра з функцією CROSSFILTER або коригування візуала.
Схема «зірка» проти «сніжинки»
Схема «зірка» (star schema) — це модель, де в центрі одна таблиця фактів, а навколо неї — таблиці вимірів, кожна з яких з’єднана з фактом напряму зв’язком 1:*. Візуально це нагадує зірку: факт у центрі, промені — виміри.
Для нашого магазину зірка виглядає так: у центрі — факт продажів (order_items, збагачений даними замовлення), навколо — customers, products і Календар. Кожен вимір фільтрує факт напряму, без посередників.
Схема «сніжинка» (snowflake) — це коли виміри нормалізовані далі: наприклад, з products винесено окрему таблицю categories, а з customers — таблицю cities. Виходить ланцюжок: categories → products → order_items. Так роблять у класичних сховищах даних, щоб уникнути дублювання, але в Power BI довші ланцюжки зв’язків означають повільнішу фільтрацію і складнішу модель.
Практична порада для Power BI: «сплющуйте» сніжинку до зірки ще в Power Query — приєднайте назву категорії прямо в таблицю products. Стовпчикове стискання VertiPaq зробить так, що повторювані текстові значення майже не займатимуть місця, а модель стане простішою і швидшою.
Календарна таблиця: обов’язковий вимір
Окремий вимір дат потрібен майже кожній моделі. Без нього не працюватимуть функції time intelligence (порівняння з минулим роком, накопичення з початку року), а вбудована «авто-дата» Power BI створює приховані таблиці для кожного стовпця з датою і роздуває файл.
Створіть календарну таблицю через «Modeling → New table» за допомогою DAX:
Календар =
ADDCOLUMNS (
CALENDAR ( DATE ( 2023, 1, 1 ), DATE ( 2025, 12, 31 ) ),
"Рік", YEAR ( [Date] ),
"Номер місяця", MONTH ( [Date] ),
"Місяць", FORMAT ( [Date], "MMMM" ),
"Квартал", "Кв " & FORMAT ( [Date], "Q" ),
"Рік-Місяць", FORMAT ( [Date], "YYYY-MM" )
)Далі три кроки: 1) позначте таблицю як таблицю дат («Mark as date table», стовпець Date); 2) створіть зв’язок Календар[Date] → orders[order_date]; 3) вимкніть «Auto date/time» у Options → Data Load. Для правильного сортування назв місяців задайте стовпцю «Місяць» параметр «Sort by column» → «Номер місяця».
Діапазон дат у CALENDAR підлаштуйте під свої дані: він має покривати всі дати у фактах — від найранішого замовлення до кінця поточного року.
Оптимізація моделі: коротко про головне
Добре змодельована зірка — вже 80% оптимізації. Решта — гігієна:
- Видаліть зайві стовпці ще у Power Query: технічні ідентифікатори, службові поля, все, що не потрібно для звіту. Найбільше «важать» стовпці з високою унікальністю.
- Приховайте ключі зв’язків (customer_id, product_id) у вкладці Model — користувачі звіту не повинні тягнути їх у візуали.
- Слідкуйте за типами даних: цілі числа стискаються краще за текст; дата без часу — краще за datetime.
- Уникайте обчислюваних стовпців DAX там, де розрахунок можна зробити в Power Query або SQL: такі стовпці гірше стискаються.
- Не тягніть деталізацію, яка не потрібна: якщо звіт працює по днях, не імпортуйте дані з точністю до секунди.
Поширені помилки
- Одна «широка» таблиця замість моделі. Працює на 10 тис. рядків, але ламається на мільйоні: файл роздувається, а розрахунки на кшталт «унікальні клієнти» стають ненадійними.
- Зв’язок за текстовим полем. Пов’язувати таблиці за назвою товару чи ім’ям клієнта небезпечно: дублікати й одруки зламають кардинальність. Використовуйте id.
- Двоспрямовані зв’язки «про всяк випадок». Both скрізь — це неоднозначні шляхи фільтрації та повільний звіт. Стандарт — Single від виміру до факту.
- Немає календарної таблиці. «Авто-дата» здається зручною, поки вам не знадобиться порівняти продажі рік до року — тоді без власного календаря не обійтися.
- Багато-до-багатьох через дублікати. Power BI сам запропонує *:*, якщо у довіднику задвоєні ключі. Правильне рішення — прибрати дублікати, а не приймати запропоновану кардинальність.
- Виміри, приєднані один до одного. Ланцюжки «вимір → вимір → факт» (сніжинка) ускладнюють модель. Об’єднуйте атрибути в один вимір у Power Query.
FAQ
Чим таблиця фактів відрізняється від таблиці вимірів?
Факт зберігає події з числами для агрегації (замовлення, суми, кількість), вимір — атрибути для фільтрації та групування (клієнт, товар, дата). Факти великі й ростуть щодня, виміри компактні. У моделі зв’язок завжди йде від виміру (сторона «один») до факту (сторона «багато»).
Чи можна обійтися без схеми «зірка» в маленькому звіті?
Можна: якщо у вас одна таблиця на кілька тисяч рядків і три візуали, плоска таблиця спрацює. Але щойно з’являються друге джерело, календар чи потреба в time intelligence — зірка стає найпростішим шляхом. Звичка будувати її одразу економить години на переробках.
Коли доречний зв’язок «багато-до-багатьох»?
У рідкісних сценаріях: наприклад, бюджет задано по категоріях і місяцях, а факти — по товарах і днях, і спільного ключа немає. Тоді *:* або проміжна таблиця-міст — свідоме рішення. Якщо ж Power BI пропонує *:* для зв’язку «довідник — факт», у довіднику дублікати, і їх треба усунути.
Навіщо календарна таблиця, якщо в orders уже є order_date?
Стовпець order_date містить лише дати, у які були замовлення, — з пропусками. Календарна таблиця містить безперервний ряд дат плюс атрибути (рік, квартал, місяць), спільні для всіх фактів моделі. Саме на безперервному календарі коректно працюють TOTALYTD, SAMEPERIODLASTYEAR та інші функції time intelligence, які ми розберемо в наступному уроці.
Що далі
Ви навчилися будувати модель: розділяти факти й виміри, налаштовувати зв’язки з правильною кардинальністю та напрямом фільтрації, збирати схему «зірка» і створювати календарну таблицю. Тепер будь-який зріз і фільтр у вашому звіті працюватиме передбачувано.
Наступний крок — обчислення: у уроці 8 «DAX для початківців: міри, CALCULATE та контекст» ви напишете перші міри й побачите, наскільки простим стає DAX на правильній моделі. Якщо ще не зібрали свій перший звіт — поверніться до уроку 6 «Дашборд у Power BI з нуля: покрокова інструкція». Повна програма — на сторінці курсу аналітики даних.
Це урок 7 з 9 курсу аналітики даних.
Уроки виходять щодня о 09:30 — програма і навігація на сторінці курсу.