T-SQL 缓慢变化的维度 - 多次更新,单次动作输出?
T-SQL Slowly Changing Dimension - Multiple Updates, Single Action Output?
我已经为我正在处理的仓库实施了四个 SCD,他们正在努力工作。希望我已经正确理解了 SCD 方法,假设我已经理解了,我现在有一个问题想要帮助解决。
问题在于有些字段无需创建新维度即可更新,因为它们与时间无关。所以在 MERGE 语句中我想有两个语句:
WHEN MATCHED AND tgt.HashBytes_Value <> src.HashBytes_Value
THEN
UPDATE Row_Expiry_Details
WHEN MATCHED AND tgt.HashBytes_Value = src.HashBytes_Value
THEN
UPDATE Columns_That_Are_Not_SCD_Relevant
etc..
我不确定如何向查询添加第二个 WHEN MATCHED。
这是一个代码示例,运行现在可以正常运行,但未达到我的要求。
-- Created temp tables for example
DROP TABLE IF EXISTS #StagingTable
CREATE TABLE #StagingTable
(
id INT,
name VARCHAR(20),
team VARCHAR(20),
preferences VARCHAR(50),
hashbytes_Value VARBINARY(20)
)
DROP TABLE IF EXISTS #PresTable
CREATE TABLE #PresTable
(
dim_id INT IDENTITY(1,1) PRIMARY KEY,
id INT,
name VARCHAR(20),
team VARCHAR(20),
preferences VARCHAR(50),
hashbytes_Value VARBINARY(20),
row_Current_ind BIT DEFAULT(1),
row_effective_date DATETIME2 DEFAULT(SYSDATETIME()),
row_expiry_date DATETIME2 DEFAULT(SYSDATETIME())
)
-- Load Staging
insert into #StagingTable (id, name, team, preferences, hashbytes_Value)
select id = 1, name = 'archibald', team = 'team 1', preferences = 'Throwaway String', hashbytes_value = hashbytes('sha1', (select team = 'team 1', name = 'archibald' for xml raw))
union select id = 2, name = 'dave', team = 'team 1', preferences = 'Throwaway String', hashbytes_value = hashbytes('sha1', (select team = 'team 1', name = 'dave' for xml raw))
union select id = 3, name = 'peter', team = 'team 2', preferences = 'Throwaway String', hashbytes_value = hashbytes('sha1', (select team = 'team 2', name = 'peter' for xml raw))
union select id = 4, name = 'roger', team = 'team 2', preferences = 'Throwaway String', hashbytes_value = hashbytes('sha1', (select team = 'team 2', name = 'roger' for xml raw))
-- scd merge
insert into #PresTable (id, name, team, preferences, hashbytes_Value)
select id, name, team, preferences, hashbytes_Value
from
(
merge into #PresTable as tgt
using
(
select
id
,name
,team
,preferences
,hashbytes_value
from
#StagingTable
) as src
-- alias
(id, name, team, preferences, hashbytes_Value)
on src.id = tgt.id
-- must be most recent row
and tgt.row_current_ind = 1
when matched
and tgt.hashbytes_value <> src.hashbytes_value
then
update
set
tgt.row_current_ind = 0
,row_expiry_date = sysdatetime()
when not matched
then
insert
(
id
,name
,team
,preferences
,hashbytes_Value
)
values
(
src.id
,src.name
,src.team
,src.preferences
,src.hashbytes_value
)
output
$action
,src.id
,src.name
,src.team
,src.preferences
,src.hashbytes_value
)
as changes
(
action
,id
,name
,team
,preferences
,hashbytes_Value
) where action = 'update'
truncate table #StagingTable
insert into #StagingTable (id, name, team, preferences, hashbytes_Value)
select id = 1, name = 'archibald', team = 'team 1', preferences = 'Updated Throwaway String', hashbytes_value = hashbytes('sha1', (select team = 'team 1', name = 'archibald' for xml raw))
union select id = 3, name = 'peter', team = 'team 1', preferences = 'Updated Throwaway String', hashbytes_value = hashbytes('sha1', (select team = 'team 1', name = 'peter' for xml raw))
union select id = 5, name = 'russell', team = 'team 2', preferences = 'Updated Throwaway String', hashbytes_value = hashbytes('sha1', (select team = 'team 2', name = 'roger' for xml raw))
-- scd merge
insert into #PresTable (id, name, team, preferences, hashbytes_Value)
select id, name, team, preferences, hashbytes_Value
from
(
merge into #PresTable as tgt
using
(
select
id
,name
,team
,preferences
,hashbytes_value
from
#StagingTable
) as src
-- alias
(id, name, team, preferences, hashbytes_Value)
on src.id = tgt.id
-- must be most recent row
and tgt.row_current_ind = 1
when matched
and tgt.hashbytes_value <> src.hashbytes_value
then
update
set
tgt.row_current_ind = 0
,row_expiry_date = sysdatetime()
when not matched
then
insert
(
id
,name
,team
,preferences
,hashbytes_Value
)
values
(
src.id
,src.name
,src.team
,src.preferences
,src.hashbytes_value
)
output
$action
,src.id
,src.name
,src.team
,src.preferences
,src.hashbytes_value
)
as changes
(
action
,id
,name
,team
,preferences
,hashbytes_Value
) where action = 'update'
select * from #PresTable
最终输出应该如您所见,但首选项应该是 'Updated Throwaway String',其中 id = 1。
在同一个合并语句中这可能吗?还是我需要更新 before/after I 运行 合并语句?
Post有人编辑了,但这是我最后想看到的例子:
UPDATE TGT
SET
TGT.preferences = SRC.preferences
FROM
#PresTable TGT
INNER JOIN
#StagingTable SRC
ON SRC.id = TGT.id
WHERE 1=1
AND TGT.hashbytes_Value = SRC.hashbytes_Value
select * from #PresTable
谢谢。
MERGE 文档说(强调我的):
WHEN MATCHED THEN merge_matched
Specifies that all rows of *target_table, which match the rows
returned by ON , and satisfy
any additional search condition, are either updated or deleted
according to the clause.
The MERGE statement can have, at most, two WHEN MATCHED clauses. If
two clauses are specified, the first clause must be accompanied by an
AND clause. For any given row, the second WHEN
MATCHED clause is only applied if the first isn't. If there are two
WHEN MATCHED clauses, one must specify an UPDATE action and one must
specify a DELETE action. When UPDATE is specified in the
clause, and more than one row of
matches a row in target_table based on , SQL
Server returns an error. The MERGE statement can't update the same row
more than once, or update and delete the same row.
所以你不能在匹配条件上发出 2 个不同的 UPDATE
。您可以做的是使用 1 UPDATE
并有条件地将列更新为新值或它们的当前值(如果您的条件不适用):
WHEN MATCHED THEN UPDATE TableName SET
Column1 = CASE WHEN tgt.HashBytes_Value <> src.HashBytes_Value THEN UpdatedColumn1 ELSE Column1 END,
Column2 = CASE WHEN tgt.HashBytes_Value <> src.HashBytes_Value THEN UpdatedColumn2 ELSE Column2 END,
Column3 = CASE WHEN tgt.HashBytes_Value <> src.HashBytes_Value THEN Column3 ELSE UpdatedColumn3 END
请记住,将为实际上可能未获得 "updated".
的行调用触发器
我已经为我正在处理的仓库实施了四个 SCD,他们正在努力工作。希望我已经正确理解了 SCD 方法,假设我已经理解了,我现在有一个问题想要帮助解决。
问题在于有些字段无需创建新维度即可更新,因为它们与时间无关。所以在 MERGE 语句中我想有两个语句:
WHEN MATCHED AND tgt.HashBytes_Value <> src.HashBytes_Value
THEN
UPDATE Row_Expiry_Details
WHEN MATCHED AND tgt.HashBytes_Value = src.HashBytes_Value
THEN
UPDATE Columns_That_Are_Not_SCD_Relevant
etc..
我不确定如何向查询添加第二个 WHEN MATCHED。
这是一个代码示例,运行现在可以正常运行,但未达到我的要求。
-- Created temp tables for example
DROP TABLE IF EXISTS #StagingTable
CREATE TABLE #StagingTable
(
id INT,
name VARCHAR(20),
team VARCHAR(20),
preferences VARCHAR(50),
hashbytes_Value VARBINARY(20)
)
DROP TABLE IF EXISTS #PresTable
CREATE TABLE #PresTable
(
dim_id INT IDENTITY(1,1) PRIMARY KEY,
id INT,
name VARCHAR(20),
team VARCHAR(20),
preferences VARCHAR(50),
hashbytes_Value VARBINARY(20),
row_Current_ind BIT DEFAULT(1),
row_effective_date DATETIME2 DEFAULT(SYSDATETIME()),
row_expiry_date DATETIME2 DEFAULT(SYSDATETIME())
)
-- Load Staging
insert into #StagingTable (id, name, team, preferences, hashbytes_Value)
select id = 1, name = 'archibald', team = 'team 1', preferences = 'Throwaway String', hashbytes_value = hashbytes('sha1', (select team = 'team 1', name = 'archibald' for xml raw))
union select id = 2, name = 'dave', team = 'team 1', preferences = 'Throwaway String', hashbytes_value = hashbytes('sha1', (select team = 'team 1', name = 'dave' for xml raw))
union select id = 3, name = 'peter', team = 'team 2', preferences = 'Throwaway String', hashbytes_value = hashbytes('sha1', (select team = 'team 2', name = 'peter' for xml raw))
union select id = 4, name = 'roger', team = 'team 2', preferences = 'Throwaway String', hashbytes_value = hashbytes('sha1', (select team = 'team 2', name = 'roger' for xml raw))
-- scd merge
insert into #PresTable (id, name, team, preferences, hashbytes_Value)
select id, name, team, preferences, hashbytes_Value
from
(
merge into #PresTable as tgt
using
(
select
id
,name
,team
,preferences
,hashbytes_value
from
#StagingTable
) as src
-- alias
(id, name, team, preferences, hashbytes_Value)
on src.id = tgt.id
-- must be most recent row
and tgt.row_current_ind = 1
when matched
and tgt.hashbytes_value <> src.hashbytes_value
then
update
set
tgt.row_current_ind = 0
,row_expiry_date = sysdatetime()
when not matched
then
insert
(
id
,name
,team
,preferences
,hashbytes_Value
)
values
(
src.id
,src.name
,src.team
,src.preferences
,src.hashbytes_value
)
output
$action
,src.id
,src.name
,src.team
,src.preferences
,src.hashbytes_value
)
as changes
(
action
,id
,name
,team
,preferences
,hashbytes_Value
) where action = 'update'
truncate table #StagingTable
insert into #StagingTable (id, name, team, preferences, hashbytes_Value)
select id = 1, name = 'archibald', team = 'team 1', preferences = 'Updated Throwaway String', hashbytes_value = hashbytes('sha1', (select team = 'team 1', name = 'archibald' for xml raw))
union select id = 3, name = 'peter', team = 'team 1', preferences = 'Updated Throwaway String', hashbytes_value = hashbytes('sha1', (select team = 'team 1', name = 'peter' for xml raw))
union select id = 5, name = 'russell', team = 'team 2', preferences = 'Updated Throwaway String', hashbytes_value = hashbytes('sha1', (select team = 'team 2', name = 'roger' for xml raw))
-- scd merge
insert into #PresTable (id, name, team, preferences, hashbytes_Value)
select id, name, team, preferences, hashbytes_Value
from
(
merge into #PresTable as tgt
using
(
select
id
,name
,team
,preferences
,hashbytes_value
from
#StagingTable
) as src
-- alias
(id, name, team, preferences, hashbytes_Value)
on src.id = tgt.id
-- must be most recent row
and tgt.row_current_ind = 1
when matched
and tgt.hashbytes_value <> src.hashbytes_value
then
update
set
tgt.row_current_ind = 0
,row_expiry_date = sysdatetime()
when not matched
then
insert
(
id
,name
,team
,preferences
,hashbytes_Value
)
values
(
src.id
,src.name
,src.team
,src.preferences
,src.hashbytes_value
)
output
$action
,src.id
,src.name
,src.team
,src.preferences
,src.hashbytes_value
)
as changes
(
action
,id
,name
,team
,preferences
,hashbytes_Value
) where action = 'update'
select * from #PresTable
最终输出应该如您所见,但首选项应该是 'Updated Throwaway String',其中 id = 1。
在同一个合并语句中这可能吗?还是我需要更新 before/after I 运行 合并语句?
Post有人编辑了,但这是我最后想看到的例子:
UPDATE TGT
SET
TGT.preferences = SRC.preferences
FROM
#PresTable TGT
INNER JOIN
#StagingTable SRC
ON SRC.id = TGT.id
WHERE 1=1
AND TGT.hashbytes_Value = SRC.hashbytes_Value
select * from #PresTable
谢谢。
MERGE 文档说(强调我的):
WHEN MATCHED THEN merge_matched
Specifies that all rows of *target_table, which match the rows returned by ON , and satisfy any additional search condition, are either updated or deleted according to the clause.
The MERGE statement can have, at most, two WHEN MATCHED clauses. If two clauses are specified, the first clause must be accompanied by an AND clause. For any given row, the second WHEN MATCHED clause is only applied if the first isn't. If there are two WHEN MATCHED clauses, one must specify an UPDATE action and one must specify a DELETE action. When UPDATE is specified in the clause, and more than one row of matches a row in target_table based on , SQL Server returns an error. The MERGE statement can't update the same row more than once, or update and delete the same row.
所以你不能在匹配条件上发出 2 个不同的 UPDATE
。您可以做的是使用 1 UPDATE
并有条件地将列更新为新值或它们的当前值(如果您的条件不适用):
WHEN MATCHED THEN UPDATE TableName SET
Column1 = CASE WHEN tgt.HashBytes_Value <> src.HashBytes_Value THEN UpdatedColumn1 ELSE Column1 END,
Column2 = CASE WHEN tgt.HashBytes_Value <> src.HashBytes_Value THEN UpdatedColumn2 ELSE Column2 END,
Column3 = CASE WHEN tgt.HashBytes_Value <> src.HashBytes_Value THEN Column3 ELSE UpdatedColumn3 END
请记住,将为实际上可能未获得 "updated".
的行调用触发器