SCD 类型 2 - 处理日内变化?

SCD Type 2 - Handling Intraday changes?

我有一个合并语句,每晚构建我的 SCD 类型 2 table。此 table 必须包含在源系统中所做的所有历史更改,并创建一个日期为 from/date 的新行到填充的列以及“islatest”标志。我今天遇到一个问题,我不太确定如何处理。

似乎在 24 小时内对源 table 进行了多次更改。

    ID      Code         PAN     EnterDate   Cost     Created
  16155 1012401593331   ENRD    2015-11-05  7706.3  2021-08-17 14:34
  16155 1012401593331   ENRD    2015-11-05  8584.4  2021-08-17 16:33

我使用基本的合并语句来识别我的更改,但是确保正确获取所有更改的最佳方法是什么?上面给了我一个错误,因为它试图 insert/update 具有相同值的多行

DECLARE @DateNow DATETIME = Getdate()
IF Object_id('tempdb..#meteridinsert') IS NOT NULL
DROP TABLE #meteridinsert;

CREATE TABLE #meteridinsert
             (
                          meterid INT,
                          change  VARCHAR(10)
             );
             
             
             MERGE
INTO         [DIM].[Meters] AS target
using        stg_meters     AS source
ON target.[ID] = source.[ID]
AND          target.latest=1
WHEN matched THEN
UPDATE
SET              target.islatest = 0,
                 target.todate = @Datenow
WHEN NOT matched BY target THEN
INSERT
       (
              id,
              code,
              pan,
              enterdate,
              cost,
              created,
              [FromDate] ,
              [ToDate] ,
              [IsLatest]
       )
       VALUES
       (
              source.id,
              source.code ,
              source.pan ,
              source.enterdate ,
              source.cost ,
              source.created ,
              @Datenow ,
              NULL ,
              1
       )
       output source.id,
       $action
INTO   #meteridinsert;INSERT INTO [DIM].[Meters]
            (
                        [id] ,
                        [code] ,
                        [pan] ,
                        [enterdate] ,
                        [cost] ,
                        [created] ,
                        [FromDate] ,
                        [ToDate] ,
                        [IsLatest]
            )
SELECT     ([id] ,[code] ,[pan] ,[enterdate] ,[cost] ,[created] , @DateNow  ,NULL ,1 FROM stg_meters a
INNER JOIN #meteridinsert cid
ON         a.id = cid.meterid
AND        cid.change = 'UPDATE'

也许你可以使用 merge 语句来做到这一点,但我更愿意使用典型的 updateinsert 方法以使其更容易理解(我也不是确保 merge 允许您使用相同的源记录进行更新和插入...)

首先我创建 table dimscd2 来代表你的维度 table

create table dimscd2 
(naturalkey int, descr varchar(100), startdate datetime, enddate datetime)

然后我插入一些记录...

insert into dimscd2 values
(1,'A','2019-01-12 00:00:00.000', '2020-01-01 00:00:00.000'),
(1,'B','2020-01-01 00:00:00.000', NULL)

如您所见,“当前”是带有 descr='B' 的那个,因为它有一个 enddate NULL(我建议您使用 代理键 for each record... 这只是你维度的每条记录的增量键,而事实table必须与这个代理键链接才能反映事实在发生的那一刻的状态).

然后,我创建了一些虚拟数据来表示具有相同自然键更改的源数据

-- new data (src_data)
select 1 as naturalkey,'C' as descr, cast('2020-01-02 00:00:00.000' as datetime) as dt into src_data
union all
select 1 as naturalkey,'D' as descr, cast('2020-01-03 00:00:00.000' as datetime) as dt

之后,我用这个查询创建了一个临时 table (##tmp) 来为每条记录设置 enddate:

-- tmp table
select naturalkey, descr, dt, 
  lead(dt,1,0) over (partition by naturalkey order by dt) enddate,
  row_number() over (partition by naturalkey order by dt) rn
into ##tmp  
from src_data 

LEAD 函数采用同一自然键的下一个开始日期,按日期排序 (dt)。 ROW_NUMBER 标记为维度中自然键的源数据中最旧的记录 1

然后,我继续使用 update

关闭“当前”记录
update d
set enddate = t.dt
from dimscd2 d
  join ##tmp t
    on d.naturalkey = t.naturalkey
   and d.enddate is null
   and t.rn = 1

最后,我将新的源数据添加到具有 insert

的维度
insert into dimscd2
select naturalkey, descr, dt, 
  case enddate when '1900-00-00' then null else enddate end 
from ##tmp

查询得到最终结果:

select * from dimscd2

你可以测试这个db<>fiddle