|
|
|||||||||||||||||||||||||||||
|
Методика формирования измерения с атрибутами типа 1 и 2Источник: habrahabr DataArms
Мы работаем над DWH в телекоммуникациях, поэтому пример, который я рассматриваю, называется "Абонент". Принцип универсален и это мог быть "Клиент" или "Пациент" - в зависимости от отрасли. Я надеюсь методику найдут полезной разработчики DWH из разных отраслей. Если Вы не понимаете, что такое DWH, измерения и факты, я рекомендую прочитать книгу Ральфа Кимбалла "Dimensional Modeling". Речь идёт о базе данных для аналитики и консолидированной отчетности предприятия, конкретно о формировании и актуализации измерений - таблиц, которые хранят атрибуты (поля) для отбора (WHERE) в будущих запросах. Наша методика предназначена для Microsoft SQL Server.
Принцип определения измененийОпределение изменения атрибутов типа 1 (перезаписываемый) и 2 (с хранением истории в записях измерения) выполняется на основе сравнения контрольных сумм полей. Для вычисления контрольных сумм используется функция T-SQL CHECKSUM, не поддерживающая типы text, ntext, image, которые и не должны помещаться в измерения. Использование BINARY_CHECKSUM на практике показало, что возможно ложное детектирование изменений в полях, содержащих NULL. С данной методикой возможно использование кастомных функций контрольной суммы, разработанных на .NET.
Объявление измеренияИзмерение должно быть объявлено с первичным ключом, содержащим кластерный индекс. Пример SQL скрипта объявления измерения "Абонент":
Нужно наложить на измерение условный индекс по бизнес-ключу с условием [EndTime] IS NULL, содержащий поля контрольных сумм. Включение в индекс контрольных сумм, при условии кластерного индекса первичного ключа таблицы измерения ([AccountKey]), позволяет не считывать саму таблицу измерения при выполнении начального запроса. При этом, индекс выполняет функцию контроля уникальности - одна действующая запись для одного бизнес-ключа. Пример индекса для измерения "Абонент":
Типовой начальный SQL запросНачальный SQL запрос содержит внутренний подзапрос [i], который получает поля из исходных таблиц-копий и внешний запрос [o], который формирует контрольные суммы, и присоединение актуальной строки измерения по бизнес-ключу. Пример начального запроса для измерения "Абонент":
Приведение типов (это как правило CAST(… AS NVARCHAR(..)) или IIF(ISDATE([...]) = 1, CAST([...] AS DATE), NULL)), а так же всё связывание исходных таблиц (LEFT JOIN) и условное формирование полей (CASE, IIF) нужно делать во внутреннем запросе - внутри FROM (...) AS [i]. Если логика связывания исходных таблиц слишком сложная (например, нужно вытащить какие-то данные из иерархии) и её невозможно выполнить внутри FROM, тогда перед потоком данных в SSIS, Вам придется вставить SQL Task, формирующий промежуточные данные в отдельных таблицах (соблюдая вашу схему именования объектов). Временные таблицы не подойдут, поскольку по ним SSIS не сможет определить метаданные выходного потока. Убедиться в корректности взаимодействия индекса и начального запроса можно, посмотрев его план выполнения. В конце плана выполнения не должно быть обращения к таблице:
Типовой поток данныхПоток данных формирования измерения выходит из начального SQL запроса, описанного выше, и реализует дальнейшую логику изменения.
В операции "Добавление StartTime" добавляем в поток StartTime, используя время запуска пакета SSIS (берем переменную @[System::StartTime]) В операции "Выявить обновление типа 1" выделяем один поток - "Обновлены поля Type1" на основе выражения !ISNULL(OLD_CHECKSUM1) && CHECKSUM1 != OLD_CHECKSUM1. В операции "Обновить поля типа 1 по бизнес-ключу" обновляем все записи измерения (в том числе уже закрытые записи, содержащие исторические значения полей типа 2), в которых изменились атрибуты первого типа - для этого используем бизнес-ключ без условия отсечения неактуальных записей (без условия по [EndTime]). Пример для "Абонент":
В операции "Выявить обновление типа 2 и новые записи" выделяем два потока:
В операции "Закрыть изменившиеся строки измерения" обновляем [EndTime] для записей измерения значением StartTime из потока. Пример для "Абонент":
В операции "Вставить строки в измерение" вставляем новые строки, при этом, в поле [StartTime] вставляем StartTime из потока, ключ измерения и [EndTime] игнорируем (NULL образующийся в поле [EndTime] будет признаком актуальной записи). При вставке на последней операции не получится использовать режим Fast Load потому, что вставка, выполняемая в одном потоке с обновлениями, должна оперировать строкой, не расширяя блокировку до уровня таблицы, иначе будут конфликты между одновременно выполняемыми операциями. Альтернативно, Вы можете разнести операции по разным шагам управляющего потока, сохраняя промежуточные результаты в Raw или Cache и соблюдая порядок операций.
Использование транзакцииНа уровне контейнера потока данных (или общего контейнера, если Вы разнесли операции по шагам управляющего потока) желательно включить транзакцию. Для этого установите TransactionOption = Required (требует DTC) и IsolationLevel не ниже ReadCommitted. Если транзакции не будет, при прерывании потока данных часть записей измерения может остаться закрытой без вставки соответствующих актуальных записей. При следующем запуске, отсутствующие записи будут вставлены как новые, но со стартовым временем, отличающимся от времени закрытия предыдущей строки. Это следует учитывать, если факты привязываются к измерению способом присоединения записи актуальной на момент возникновения факта - оперировать при привязке измерений только временем закрытия.
Сравнение с другими методамиПо сравнению с использованием стандартного компонента SQL Server Integration Services, под названием Slowly Changing Dimension, данный метод не использует сравнение каждого поля с каждым полем в строке измерения - он даже не обращается к таблице для выполнения такого сравнения. Это дает основное преимущество - скорость. Кроме того, стандартный Slowly Changing Dimension управляет сразу целой цепочкой элементов и это создает сложность с их кастомизацией. По неизвестным мне причинам, стандартный компонент SSIS может ложно определять изменения (возможно, это так же связано с полями NULL). Перед коммерческими компонентами сторонних поставщиков есть принципиальное преимущество метода в том, что он базируется на стандартных компонентах и функциях поставляемых с выпуском SQL Server. Таким образом, не требуется ожидать обновленных компонентов для перехода на следующую версию SQL Server.
Модификации методаВозможен модифицированный метод, при котором присоединение выполняется не в SQL запросе, а в потоке данных SSIS через в операцию Lookup. Это приводит к большему количеству обращений к базе и замене эффективного Hash Match на менее эффективные одиночные запросы. С другой стороны, это позволяет разделить исходные таблицы (таблицы-копии, формируемые на стадии загрузки из исходных систем) и таблицы измерений на разные сервера. Но польза от такой возможности у меня вызывает сомнения. Возможно присоединение в потоке SSIS через операцию Merge Join, но она потребует извлечения таблицы и её сортировки, что сведет на нет преимущества индекса. Расчёт контрольной суммы так же возможен в потоке данных, например отдельным компонентом, но в этом случае исчезает преимущество отсутствия необходимости в отдельных компонентах. Расчёт контрольной суммы в потоке данных с помощью Script Transformation на C# - возможный вариант модификации, если в этом усложнении есть смысл. Ссылки по теме
|
|