Data Analyst Professional · Модуль 4 · Урок 12 із 51

Ключі й безпечний JOIN без множення фактів

JOIN з’єднує рядки за умовою, але не знає вашого expected grain. Якщо праворуч кілька відповідностей, факти зліва повторяться. Тому до синтаксису потрібні cardinality contract, перевірка ключів і reconciliation після кожного з’єднання.

90–120 хвJoin cardinalityМінітест: 3 питання

Почніть із cardinality contract

Зв’язокПрикладЩо стається з рядкамиОсновний ризик
one-to-oneorder → approved_order_profile0 або 1 matchнеочікувані дублікати ключа праворуч
many-to-oneorders → customersкожне order має максимум 1 customerнеунікальний customer_id у dimension
one-to-manyorders → order_itemsorder повторюється для кожної позиціїdouble counting order-level amount
many-to-manycampaigns ↔ tagsкомбінації множатьсявибух рядків без bridge/aggregation rule

INNER і LEFT JOIN відповідають на різні питання

-- Лише замовлення з наявним клієнтом SELECT o.order_id, c.segment FROM analytics.orders AS o INNER JOIN analytics.customers AS c ON c.customer_id = o.customer_id;-- Усі замовлення, навіть якщо профіль клієнта відсутній SELECT o.order_id, c.segment FROM analytics.orders AS o LEFT JOIN analytics.customers AS c ON c.customer_id = o.customer_id;

Для LEFT JOIN unmatched поля праворуч стають NULL. Якщо після цього фільтрувати WHERE c.segment = 'B2B', unmatched rows зникнуть, і поведінка наблизиться до INNER JOIN. Умову треба розміщувати відповідно до business intent.

Перевірки до JOIN

Left grain

Скільки rows і distinct expected key має базова вибірка?

Right uniqueness

Чи унікальний join key праворуч, якщо очікується many-to-one?

NULL keys

Скільки NULL та порожніх ключів з обох боків?

Coverage

Скільки left keys matched, unmatched і matched multiple times?

SELECT customer_id, COUNT(*) AS rows_per_key FROM analytics.customers GROUP BY customer_id HAVING COUNT(*) > 1;

Fan-out: найнебезпечніша тиха помилка

Якщо сума orders.net_amount зберігається на grain order, після JOIN з order_items вона повториться на кожній позиції. SUM(o.net_amount) завищить виручку.

Варіант 1Рахуйте item-level amount із quantity × price, якщо це визначена метрика.
Варіант 2Спочатку агрегуйте items до order_id, потім приєднуйте one row per order.
Варіант 3Залиште order-level KPI в окремому query/grain.
Не робітьSUM(DISTINCT net_amount): однакові суми різних orders будуть помилково схлопнуті.

Reconciliation після JOIN

КонтрольДоПісляІнтерпретація
row count10 000 orders12 840 rowsє one-to-many або duplicate matches
distinct order_id10 0009 98020 orders втрачено через INNER JOIN/filter
unmatched73quality issue або очікувана відсутність dimension
control total₴5,2 млн₴6,8 млнфакт повторився; висновок заборонений

Практика: join audit

  • Запишіть grain і expected cardinality обох таблиць.
  • Перевірте дублікати та NULL join key праворуч.
  • Виконайте LEFT JOIN і позначте matched/unmatched.
  • Звірте rows, distinct left key і контрольний total до/після.
  • Якщо є fan-out, pre-aggregate праву таблицю або розділіть KPI за grain.

Офіційні довідки

Готовність до тесту

Ви можете передбачити cardinality, відрізнити INNER від LEFT JOIN, знайти fan-out і довести коректність через row/key/total reconciliation.

Практична перевірка · урок 12 з 51

Закріпіть матеріал уроку

Три сценарні питання. Для зарахування уроку потрібно дати щонайменше дві правильні відповіді.

1. Що треба визначити до JOIN?
2. Що повертає INNER JOIN?
3. Що робить LEFT JOIN з unmatched row ліворуч?