admin管理员组文章数量:1419666
I have two tables in SQL server, the first table Source, has two columns: ID, attribute1, attribute2, and attribute3. The second table, Target, has the same columns as Source but with two additional columns: valid_from and valid_to.
I want to implement the following logic for my tables in SQL Server:
If the ID does not exists in Source but not in Target, then insert the row in Target, setting valid_from to today's date, and valid_to to '9999-12-31'.
If the ID does exist but attribute is the same, nothing needs to happen.
If the ID does exists but the attribute is not the same. Then I want to update the row in Target, by setting the valid_to to yesterdays date. And then I want to insert the row from Source to target setting valid_from to todays date, and valid_to to '9999-12-31'.
I've tried to combine an update and insert into a merge statement:
MERGE INTO [Target] AS tgt
USING [Source] AS src
ON tgt.id = src.id
WHEN MATCHED AND (
tgt.attribute1 <> src.attribute1 OR
tgt.attribute2 <> src.attribute2 OR
tgt.attribute3 <> src.attribute3
) THEN
-- Close the current record and insert a new record
UPDATE SET
tgt.valid_to = FORMAT(DATEADD(day, -1, GETDATE()), 'yyyy-MM-dd')
OUTPUT
src.id,
src.attribute1,
src.attribute2,
src.attribute3,
CONVERT(VARCHAR(10), GETDATE(), 120),
'9999-12-31'
INTO norm.Moment (
id,
attribute1,
attribute2,
attribute3,
valid_from,
valid_to
)
WHEN NOT MATCHED THEN
-- Insert the new record
INSERT (
id,
attribute1,
attribute2,
attribute3,
valid_from,
valid_to
)
VALUES (
src.id,
src.attribute1,
src.attribute2,
src.attribute3,
CONVERT(VARCHAR(10), GETDATE(), 120),
'9999-12-31'
);
But this does not work as the WHEN MATCH won't do both an update and insert in the same THEN clause.
Is there a way to make this kind of merge statement work or should I split it up in some way with seperate insert and updates?
Thanks in advance for any help and let me know if I can clarify things :)
I have two tables in SQL server, the first table Source, has two columns: ID, attribute1, attribute2, and attribute3. The second table, Target, has the same columns as Source but with two additional columns: valid_from and valid_to.
I want to implement the following logic for my tables in SQL Server:
If the ID does not exists in Source but not in Target, then insert the row in Target, setting valid_from to today's date, and valid_to to '9999-12-31'.
If the ID does exist but attribute is the same, nothing needs to happen.
If the ID does exists but the attribute is not the same. Then I want to update the row in Target, by setting the valid_to to yesterdays date. And then I want to insert the row from Source to target setting valid_from to todays date, and valid_to to '9999-12-31'.
I've tried to combine an update and insert into a merge statement:
MERGE INTO [Target] AS tgt
USING [Source] AS src
ON tgt.id = src.id
WHEN MATCHED AND (
tgt.attribute1 <> src.attribute1 OR
tgt.attribute2 <> src.attribute2 OR
tgt.attribute3 <> src.attribute3
) THEN
-- Close the current record and insert a new record
UPDATE SET
tgt.valid_to = FORMAT(DATEADD(day, -1, GETDATE()), 'yyyy-MM-dd')
OUTPUT
src.id,
src.attribute1,
src.attribute2,
src.attribute3,
CONVERT(VARCHAR(10), GETDATE(), 120),
'9999-12-31'
INTO norm.Moment (
id,
attribute1,
attribute2,
attribute3,
valid_from,
valid_to
)
WHEN NOT MATCHED THEN
-- Insert the new record
INSERT (
id,
attribute1,
attribute2,
attribute3,
valid_from,
valid_to
)
VALUES (
src.id,
src.attribute1,
src.attribute2,
src.attribute3,
CONVERT(VARCHAR(10), GETDATE(), 120),
'9999-12-31'
);
But this does not work as the WHEN MATCH won't do both an update and insert in the same THEN clause.
Is there a way to make this kind of merge statement work or should I split it up in some way with seperate insert and updates?
Thanks in advance for any help and let me know if I can clarify things :)
Share Improve this question edited Jan 29 at 19:39 Dale K 27.5k15 gold badges58 silver badges83 bronze badges asked Jan 29 at 12:34 br.nzbr.nz 457 bronze badges 3 |2 Answers
Reset to default 0In this case the merge statement may not be the best way to go. While they are very cool, they have documented performance issues and limitations.
If you really want to use this merge statement you can simply add an extra insert before it to accomplish what you want to do like this...
insert into dbo.Target
(
ID
,attribute1
,attribute2
,attribute3
,valid_from
,valid_to
)
select
src.ID
,src.attribute1
,src.attribute2
,src.attribute3
,getdate()
,'9999-12-31'
from
dbo.Source src
join dbo.Target tgt on tgt.ID = src.ID
and
(
tgt.attribute1 <> src.attribute1
or tgt.attribute2 <> src.attribute2
or tgt.attribute3 <> src.attribute3
)
A simple solution could be to use a trigger on insert that will update all (theorically at most 1) unclosed entries for the attribute.
This has the advantage that manually inserted entries (circumventing your MERGE
) will still let the table consistent.
But then using Temporal Tables is the right suggestion, unless you have portability or educational purpose concerns.
Proof of concept
Here a small implementation that you can see in action in a DBFiddle:
CREATE TRIGGER SourceUpdate ON [Target]
AFTER INSERT
AS
BEGIN
-- Update old entries.
UPDATE [Target]
SET valid_to = FORMAT(DATEADD(day, -1, GETDATE()), 'yyyy-MM-dd')
FROM Inserted new
WHERE [Target].id = new.id AND [Target].valid_to = '9999-12-31';
-- Then give the new entry its definitive valid_to.
UPDATE [Target]
SET valid_from = CONVERT(VARCHAR(10), GETDATE(), 120), valid_to = '9999-12-31'
FROM Inserted new
WHERE [Target].id = new.id AND [Target].valid_to IS NULL;
END;
-- And instead of the MERGE, use a simple INSERT.
INSERT INTO [Target] (id, attribute1, attribute2, attribute3)
SELECT *
FROM [Source] src
WHERE NOT EXISTS
(
SELECT 1 FROM [Target] tgt
WHERE tgt.id = src.id
AND valid_to = '9999-12-31'
AND tgt.attribute1 = src.attribute1
AND tgt.attribute2 = src.attribute2
AND tgt.attribute3 = src.attribute3
);
Note that (I'm discovering SQL Server so) I had to use two values of valid_to (NULL
and 9999-12-31
) to distinguish the just added row from the old entries in the trigger.
本文标签: sqlImplementing SCD Type 2 with merge statementStack Overflow
版权声明:本文标题:sql - Implementing SCD Type 2 with merge statement - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1745297844a2652185.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
MERGE
can't do multiple actions per row, you would need separate statements. Have you considered using Temporal Tables, which does all this for you? – Charlieface Commented Jan 29 at 12:37