DWH-ETL-OLAP
July 25, 2019

SCD2 на TSQL

Хороший старый пост по SCD2.



Кратко, немного теории:

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

Существует три типа таких измерений:

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

Type 2 - относятся те измерения, в которых поддерживается история изменений атрибута. То есть старая запись помечается как утратившая актуальность, и добавляется новая запись с тем же идентификатором, но уже с обновленными полями. Так же указывается время действия данной записи

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

Более подробней по поводу SCD-измерений можно почитать у Ralph Kimball. Информации очень много в сети по данной тематике, так что ищите.

Теперь вернемся к реализации SCD type2 с помощью ssis .

Создаем таблицу dim_Clients в MS SQL server 2012

CREATE TABLE [dbo].[dim_Clients](
    [id] [int] IDENTITY(1,1) NOT NULL, --id клиента – уникальная запись, инкрементальная
    [ClientIDSource] [int] NOT NULL,  -- id клиента в источнике
    [NameClients] [varchar](250) NOT NULL, –- имя клиента
    [TypeClients] [int] NULL, –-тип клиента
    [KodObl] [varchar](3) NULL, –-код области
    [DateRegistration] [int] NULL, –- дата регистрации
    [DateFrom] [datetime] NOT NULL, –- дата начала действия даты с
    [DateTo] [datetime] NOT NULL, –- дата окончания действия даты до
    [IsCurrent] [bit] NOT NULL, –- является текущей датой(0 – нет, 1 – да)
 CONSTRAINT [PK_dim_Clients] PRIMARY KEY CLUSTERED 
(
    [id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

SET ANSI_PADDING OFF
GO



ALTER TABLE [dbo].[dim_Clients] ADD  CONSTRAINT [DF_dim_Clients_DateFrom]  DEFAULT ('30000101') FOR [DateFrom]
GO
Теперь нам необходима дополнительная  таблица, куда мы будем сливать данные из  исходного источника, а потом обрабатывать и сохранять в таблицу в хранилище.
 
CREATE TABLE [dbo].[dim_ClientsStaging](
    [id] [int] NOT NULL,
    [NameClients] [varchar](250) NOT NULL,
    [TypeClients] [int] NULL,
    [KodObl] [varchar](3) NULL,
    [DateRegistration] [int] NULL
) ON [PRIMARY]

GO

SET ANSI_PADDING OFF
GO

ALTER TABLE [dbo].[dim_ClientsStaging] ADD  CONSTRAINT [DF_dim_ClientsStaging_DateRegistration]  DEFAULT (datediff(day,'',getdate())) FOR [DateRegistration]
GO

Создаем новый проект в SSIS 2012. Добавляем источник откуда мы получаем данные, и источник, куда мы будем заливать данные. После создаем два пакета.

В первом пакете для работы SCD2 используем конструкцию T-SQL merge.

Первым шагом очищаем предварительную таблицу, после делаем заливку данных из источника в staging таблицу, а потом с помощью скрипта T-SQL реализуем логику заполнения для SCD2. Скрипт ниже:

  insert into dbo.dim_Clients
   (  
      [ClientIDSource]
     ,[NameClients]
     ,[TypeClients]
     ,[KodObl]
     ,[DateRegistration]
     ,[DateFrom]
     ,[DateTo]
     ,[IsCurrent]
   )
   select 
      [ClientIDSource]
     ,[NameClients]
     ,[TypeClients]
     ,[KodObl]
     ,[DateRegistration]    
      ,getdate()
      ,'30000101'
      ,1     
      from      
      (
      merge into  dim_Clients  as dst
      using  dbo.dim_ClientsStaging as src
      on
      (
       src.id=dst.[ClientIDSource]
       )
      --new records inserted
      when not matched then 
      insert (
      [ClientIDSource]
     ,[NameClients]
     ,[TypeClients]
     ,[KodObl]
     ,[DateRegistration]    
     ,[DateFrom]
     ,[DateTo]
     ,[IsCurrent]
      )
      values(
      src.id
      ,src.[NameClients]
      ,src.[TypeClients]
      ,src.[KodObl]
      ,src.[DateRegistration]
      , getdate()
      , '30000101'
      ,1
      )
      --existing
     when matched
     and  isCurrent=1
     and
     (src.[NameClients]<>dst.[NameClients]
     or src.[TypeClients]<>dst.[TypeClients]
     or src.[KodObl]<>dst.[KodObl]    
      )
     then update 
     set
     dst.IsCurrent=0,
     dst.DateTo=getdate()
     output 
      src.id as clientidsource
      ,src.[NameClients]
      ,src.[TypeClients]
      ,src.[KodObl]
      ,src.[DateRegistration]
     ,$Action as MergeAction) as  mrg 
      where mrg.MergeAction='update';