我正在尝试根据以下数据集创建价格历史记录。我有一个包含价格的每一天的记录,可以在不同时期重复。请参阅下面的输入:
价格 | 日期 |
---|---|
20 | 2023-01-01 |
20 | 2023-01-02 |
19 | 2023-01-03 |
19 | 2023-01-04 |
20 | 2023-01-05 |
20 | 2023-01-06 |
我想总结成这样的价格历史表:
价格 | 开始日期 | 结束日期 |
---|---|---|
20 | 2023-01-01 | 2023-01-02 |
19 | 2023-01-03 | 2023-01-04 |
20 | 2023-01-05 | 2023-01-06 |
任何人都可以指出我正确的方向吗?我已经能够创建范围,但是被多个范围出现相同价格的可能性绊倒了,所以这搞乱了分组。
我获取了您的示例数据并将其转换为可重现的 DML/DDL,这对解决此类问题非常有帮助。我添加了一个 ProductID 列和一些额外的数据行来演示如何使用多个 ID 来完成此操作。我使用表变量是因为要清理的东西更少,但这只是个人喜好。
DECLARE @PriceHistory TABLE (ProductID BIGINT, Price DECIMAL (10,2), Date DATE)
INSERT INTO @PriceHistory (ProductID, Price, Date) VALUES
(1, 20, '2023-01-01'), (1, 20, '2023-01-02'), (1, 20, '2023-01-03'), (1, 19, '2023-01-04'),
(1, 20, '2023-01-05'), (1, 20, '2023-01-06'), (1, 20, '2023-01-07'), (1, 20, '2023-01-08'),
(2, 30, '2023-01-01'), (2, 15, '2023-01-02'), (2, 15, '2023-01-03'), (2, 19, '2023-01-04'),
(2, 19, '2023-01-05'), (2, 20, '2023-01-06'), (2, 20, '2023-01-07'), (2, 20, '2023-01-08');
使用它,我们可以使用递归公用表表达式进行查询以获得您要查找的结果。为了分隔范围,我使用了一个 LAG 窗口函数来标记这一行是否与前一行属于同一范围。
;WITH base AS (
SELECT ProductID, Price, Date, CASE WHEN LAG(Price,1) OVER (PARTITION BY ProductID ORDER BY Date) = Price THEN 1 ELSE 0 END AS InRange
FROM @PriceHistory
), Ranges AS (
SELECT ProductID, Price, Date AS StartDate, Date AS EndDate
FROM base
WHERE InRange = 0
UNION ALL
SELECT a.ProductID, a.Price, a.StartDate, r.Date AS EndDate
FROM Ranges a
INNER JOIN Base r
ON a.ProductID = r.ProductID
AND a.EndDate = DATEADD(DAY,-1,r.Date)
AND a.Price = r.Price
)
SELECT ProductID, Price, StartDate, MAX(EndDate) AS EndDate, DATEDIFF(DAY,StartDate, MAX(EndDate))+1 AS Duration
FROM Ranges
GROUP BY ProductID, Price, StartDate
ORDER BY Ranges.ProductID, Ranges.StartDate;
产品编号 | 价格 | 开始日期 | 结束日期 | 持续时间 |
---|---|---|---|---|
1 | 20.00 | 2023-01-01 | 2023-01-03 | 3 |
1 | 19.00 | 2023-01-04 | 2023-01-04 | 1 |
1 | 20.00 | 2023-01-05 | 2023-01-08 | 4 |
2 | 30.00 | 2023-01-01 | 2023-01-01 | 1 |
2 | 15.00 | 2023-01-02 | 2023-01-03 | 2 |
2 | 19.00 | 2023-01-04 | 2023-01-05 | 2 |
2 | 20.00 | 2023-01-06 | 2023-01-08 | 3 |