Медленно изменяющиеся размеры - точная реализация SQL-запроса для получения правильных данных

Я немного новичок в разработке бизнес-аналитики / хранилищах данных, но столкнулся со старой дилеммой «Медленно меняющиеся размеры». Я много читал о типах и теории, но мало что нашел с точки зрения того, что я считаю наиболее распространенными запросами SELECT для этих реализаций.

Я сделаю свой пример простым. Допустим, у вас есть четыре причины продажи: восток, запад, север и юг. У вас есть группа продавцов, которые совершают ежедневные продажи и (возможно, раз в год) переназначают новый регион.

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

name; sales; revenue; date
John Smith; 10; 5400; 2015-02-17

Такие данные у вас есть каждый день.

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

name; region
John Smith; East
Nancy Ray; West
Claire Faust; North

Итак, директор по продажам хочет знать ежемесячный доход от продаж в Восточном регионе за май 2015 года. Вы должны выполнить запрос:

SELECT region, month(date), sum(revenue)
from Fact_Table inner join Dim_Table on name = name
where region = East and date between ....
[group by region, month(date)]

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

Теперь очевидно, что в середине года продавцы могут переехать в регионы. Или в середине месяца. Итак, вам нужно создать тип SCD, чтобы выполнить этот запрос. Лично для меня тип 2 имеет наибольший смысл. Скажем, вы это реализовали. Скажем, Джон Смит перешел с Восточного региона на Западный 15 мая 2015 г. Вы реализуете следующую таблицу:

name; region; start_date; end_date
John Smith; East; 2015-01-01; 2015-05-15
John Smith; West; 2015-5-15; 9999-12-31

Теперь тот же вопрос задает директор по продажам. Какова общая выручка от продаж на Востоке за май 2015 года? Или, более того, покажите мне итоги по регионам по месяцам за весь год. Как бы вы структурировали запрос?

SELECT region, month(date), sum(reveneue)
from Fact_Table inner join Dim_Table
on name = name
and date between start_date and end_date
group by region, month(date)

Дало бы это правильные результаты? Я предполагаю, что это может быть --- мой вопрос может быть больше похож на --- хорошо, теперь предположим, что у вас есть 1 миллион записей в таблице фактов ... будет ли это внутреннее соединение крайне неэффективным, или есть более быстрый способ достижения этот результат?

Имеет ли смысл записывать SCD (например, регион) непосредственно в «денормализованную» таблицу фактов - и, когда размерность изменяется, возможно, задним числом обновлять регионы «записи фактов» за неделю или две?


person user45867    schedule 21.08.2015    source источник
comment
Я не думаю, что ваш пример помогает, поскольку регион и продавец явно будут разными измерениями. Изложенная вами структура очень затрудняет эффективное структурирование запроса по основному вопросу, который вы хотите задать, и это показатель того, что модель неверна. Как только вам понадобится сложный запрос, вы должны заново оценить размерную структуру.   -  person David Aldridge    schedule 22.08.2015
comment
На самом деле, это довольно распространенный пример в большей части литературы по SCD. Мой пример из «реальной жизни» почти такой же. Список имен, принадлежащих различным региональным офисам (США, Европа, Азия) ... некоторые приложения не имеют прямой точки данных "регион" ... или даже точки данных "отдела" ... они кодируются отдельно . ВСЕ, что у вас есть, это имя сотрудника из приложения. «Основная ссылка», отсылающая такого сотрудника обратно в региональный офис или отдел (маркетинг или обслуживание клиентов) ... ОСНОВАНА ВРЕМЕНИ. Я не понимаю, как вы утверждаете, что модель данных неверна.   -  person user45867    schedule 22.08.2015
comment
По сути, одна ссылка, в моем примере, из дохода в регион, должна ВСЕГДА проходить сначала через сотрудника, что является обстоятельством данных. Если вы замените «регион» на «отдел», это поможет понять.   -  person user45867    schedule 22.08.2015
comment
Модель подходит для OLTP-системы, но не для размерной. Когда вы добавляете факт в таблицу, у вас уже есть имя и вы знаете, к какой области они были прикреплены, поэтому вы добавляете оба как разные ключи. Очевидно, что с точки зрения отчетности важными вопросами являются доход по регионам, доход по названию и доход по регионам и названиям. Ваша структура SCD делает второй вариант довольно сложным. В качестве альтернативы, если у вас есть синтетический ключ для измерения, просто создайте новое значение затемнения для каждой допустимой комбинации имени и региона.   -  person David Aldridge    schedule 22.08.2015
comment
Да, было бы неплохо написать правильный «регион» во время создания таблицы фактов (потому что идентификатор сотрудника будет в таблице фактов) ... однако ... я работаю исходя из предположения, что ретроактивные исправления будет необходимо. Также кто-то сменит регионы, и HR / техническая команда / наш процесс может не заметить этого в течение недели или двух. Думаю, я все еще могу прикрепить идентификатор региона к таблице фактов во время ее написания, но для сохранения целостности данных и возможности делать ретроактивные обновления мне все равно понадобится таблица «даты вступления в силу». Может быть, просто для исправления или постоянного присоединения   -  person user45867    schedule 24.08.2015


Ответы (1)


Ваша концепция верна, если ваше бизнес-требование имеет иерархию Регион-> Продавец, как показано в вашем примере.

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

Используйте иерархию измерения даты, которая включает дату-> Месяц, и вы сможете избежать запроса диапазона.

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

Миллион строк - это крохотно, проблем с производительностью ни на одной грамотной СУБД не будет :)

person Ron Dunn    schedule 23.08.2015
comment
Да, в этом есть смысл - я буду помнить эти советы. Что вы имеете в виду под иерархией измерений даты? Вы имеете в виду в таблице измерений с датами «вступления в силу» --- напишите месяц в таблице также во время вставки / записи таблицы? - person user45867; 24.08.2015
comment
Для дат у вас должно быть измерение, которое выглядит примерно так: dim_date (id, date_business_key, day, month, year, day_of_week, month_name и т.д.). Ваша таблица фактов должна содержать только ссылку внешнего ключа на таблицу dim_date. Чтобы найти продажи в данном месяце, запросите соединение вашего факта + dim_date, где год и месяц являются вашими необходимыми значениями. - person Ron Dunn; 25.08.2015
comment
Это имеет смысл, за исключением того, что, если продавец находился только на «Востоке» в течение полумесяца, с 1 июня по 15 июня .... тогда для суммирования «итога» для диапазона Востока по-прежнему требуется разбиение данных с точностью до даты, не просто месяц. - person user45867; 25.08.2015
comment
На самом деле это обрабатывается измерением даты. Я не могу выполнить форматирование здесь, поэтому надеюсь, что вы понимаете этот запрос :) выберите продавца, сумму (sale_amount) из fact_sale внутреннее соединение dim_date на dim_date.id = fact_sale.date_id внутреннее соединение dim_seller на dim_seller.id = fact_sale.seller_id где dim_date.year = 2015, dim_date.month = 6 и dim_seller.region = 'Northern'. Это даст вам общий объем продаж, осуществленных каждым продавцом, когда он осуществлял продажи в Северном регионе в июне 2015 года. - person Ron Dunn; 26.08.2015