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
语句来做到这一点,但我更愿意使用典型的 update
和 insert
方法以使其更容易理解(我也不是确保 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
我有一个合并语句,每晚构建我的 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
语句来做到这一点,但我更愿意使用典型的 update
和 insert
方法以使其更容易理解(我也不是确保 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