Посмотрите на этот пример .
Это должно быть относительно просто.
Поворачивает базовые данные в соответствии с вашими правилами.
Определяет время изменения денормализованной "строки"
Создает треугольное соединение для определения начала и конца каждого периода (то, что я называю снимком)
Затем он присоединяет эти окна к базовым данным, чтобы определить, какое состояние данных было в то время (в этот момент сводка фактически завершена)
Я думаю, что вам, возможно, придется взглянуть на механизм управления окнами - он возвращает правильные данные, но мне не нравится, как мне выглядит логика перекрытия окон - она не совсем маленькая, верно - я беспокоюсь о граничные условия.
-- SO3014289
CREATE TABLE #src (
key1 varchar(4) NOT NULL
,key2 varchar(3) NOT NULL
,key3 varchar(3) NOT NULL
,AttribCode int NOT NULL
,AttribSubCode varchar(2)
,Value varchar(10) NOT NULL
,[Start] date NOT NULL
,[End] date NOT NULL
)
INSERT INTO #src VALUES
('9750', 'C04', '789', 1, NULL, 'AAA', '1/1/2000', '12/31/9999')
,('9750', 'C04', '789', 2, NULL, 'BBB', '1/1/2000', '12/31/9999')
,('9750', 'C04', '789', 3, 'V1', 'XXXX', '1/1/2000', '12/31/9999')
,('9750', 'C04', '789', 3, 'V2', 'YYYY', '1/1/2000', '1/2/2000')
,('9750', 'C04', '789', 3, 'V2', 'YYYYY', '1/2/2000', '12/31/9999')
;WITH basedata AS (
SELECT key1 + '-' + key2 + '-' + key3 AS NK
,CASE WHEN AttribCode = 1 THEN Value ELSE NULL END AS COL1
,CASE WHEN AttribCode = 2 THEN Value ELSE NULL END AS COL2
,CASE WHEN AttribCode = 3 AND AttribSubCode = 'V1' THEN Value ELSE NULL END AS COL3
,CASE WHEN AttribCode = 3 AND AttribSubCode = 'V2' THEN Value ELSE NULL END AS COL4
,[Start]
,[End]
FROM #src
)
,ChangeTimes AS (
SELECT NK, [Start] AS Dt
FROM basedata
UNION
SELECT NK, [End] AS Dt
FROM basedata
)
,Snapshots as (
SELECT s.NK, s.Dt AS [Start], MIN(e.Dt) AS [End]
FROM ChangeTimes AS s
INNER JOIN ChangeTimes AS e
ON e.NK = s.NK
AND e.Dt > s.Dt
GROUP BY s.NK, s.Dt
)
SELECT Snapshots.NK
,MAX(COL1) AS COL1
,MAX(COL2) AS COL2
,MAX(COL3) AS COL3
,MAX(COL4) AS COL4
,Snapshots.[Start]
,Snapshots.[End]
FROM Snapshots
INNER JOIN basedata
ON basedata.NK = Snapshots.NK
AND NOT (basedata.[End] <= Snapshots.[Start] OR basedata.[Start] >= Snapshots.[End])
GROUP BY Snapshots.NK
,Snapshots.[Start]
,Snapshots.[End]