我每天都有以下格式的传入数据:
(id, dat, state, val)
其中id
是唯一实体,dat
是日期(即ISO'yyyy-MM-dd'),state
是实体的状态,{{1} }是此状态下的实体的值。
我需要将其转换为这种格式的数据:val
其中(id, state, val, dat1, dat2, month)
和dat1
是实体dat2
具有状态id
的日期戳月内state
值。
我很难找到一种正确的方法来收集和保存这种格式的数据。我知道解决方案必须满足以下要求:
1)如果val
在新的每日数据(即2017-01-27)中将id
从state
更改为st1
,则应该有一行这样的封闭期间:
st2
和状态(id=1, state=st1, val=2.5, dat1='2017-01-15', dat2='2017-01-26', month='2017-01-01')
的行如下:
st2
2)如果在2017-02-14上有一个表的新(id=1, state=st2, val=5.5, dat1='2017-01-27', dat2='2017-01-31', month='2017-01-01')
,那么应该有一个插入的行:
id
,
因此,如果每日数据中有一个新的ID,并且此ID在之前的期间不存在,我们将其出生日期作为期间的开头,并将截止日期保持为月末,直到该ID的状态发生变化为止二月份。
3)表格的设计方式应允许在插入\更新每日新数据(双时态表?)之前具有表格版本的历史记录,以便有机会丢弃错误的数据和回滚。 / p>
4)应该有一个查询表,它能够检测到相同ID的重叠日期间隔(强制ID随时间的完整性)。
5)这类数据应该有一些必要的索引。 Id应该是外键。
6)当一个新数据到达加载表并进行1),2),3)中描述的操作时,是否应该有一个触发器工作?
有人能说出这类任务的文献或已知方法吗? 我相信这是解决的任务,但我无法确定数据模型的正确名称,并找到这种数据的任何明确示例。
答案 0 :(得分:0)
下面的代码更新基于“每日数据”的实体状态表。 我已经创建了自己的样本数据,我的理解符合您的要求。
CREATE TABLE #DailyData( id INT, [state] VARCHAR( 10 ), val FLOAT, dat1 DATETIME )
CREATE TABLE #EntityState( id INT, [state] VARCHAR( 10 ), val FLOAT, dat1 DATETIME, dat2 DATETIME, [month] DATETIME )
INSERT INTO #EntityState
SELECT 1, 'st2', 4.6, '01-Jul-2017', '08-Jul-2017', '01-Jul-2017' UNION ALL
SELECT 1, 'st1', 1.2, '09-Jul-2017', '31-Jul-2017', '01-Jul-2017' UNION ALL
SELECT 2, 'st1', 2.2, '08-Jul-2017', '31-Jul-2017', '01-Jul-2017'
INSERT INTO #DailyData
SELECT 1, 'st3', 5.6, '15-Jul-2017' UNION ALL
SELECT 1, 'st2', 4.6, '01-Aug-2017' UNION ALL
SELECT 2, 'st2', 2.5, '10-Jul-2017'
SELECT 'Before', * FROM #EntityState
;WITH Q0 AS(
-- Insert current state
SELECT id, [state], val, dat1
FROM
( SELECT id, [state], val, dat1, ROW_NUMBER() OVER( PARTITION BY id ORDER BY dat1 DESC ) AS LatestDate
FROM #EntityState ) AS CurrentState
WHERE LatestDate = 1
UNION ALL
-- Combine with Daily Import
SELECT id, [state], val, dat1
FROM #DailyData
)
,
-- Add Date order column
Q1 AS (
SELECT id, [state], val, dat1, ROW_NUMBER() OVER( PARTITION BY id ORDER BY dat1 ASC ) AS DateOrder
FROM Q0
)
-- Merge new Entity states with Existing
MERGE INTO #EntityState AS T
USING
-- New Entity states
( SELECT Prev.id, Prev.[state], Prev.val, Prev.dat1, Next.dat1 AS dat2
FROM Q1 AS Prev
LEFT JOIN Q1 AS Next ON Prev.id = Next.id AND Prev.DateOrder + 1 = Next.DateOrder ) AS S
ON T.id = S.id AND T.dat1 = S.dat1 AND T.[state] = S.[state]
-- Update dat2 on last day's state
WHEN MATCHED AND T.dat2 = EOMONTH( T.dat1 )
THEN UPDATE
SET T.dat2 = S.dat2
-- Add new Entity State
WHEN NOT MATCHED BY TARGET THEN
INSERT( id, state, val, dat1, dat2, [month] )
VALUES( S.id, S.state, S.val, S.dat1, ISNULL( S.dat2, EOMONTH( S.dat1 )),
DATEADD( month, DATEDIFF( month, 0, S.dat1 ), 0 ));
SELECT 'After', * FROM #EntityState