获取价格历史数据的非重叠日期范围

时间:2010-06-14 14:30:17

标签: sql-server

我们假设我有下表:

CREATE TABLE [dbo].[PricesHist]
(
   [Product] [varchar](6) NOT NULL,
   [Price] [float] NOT NULL,
   [StartDate] [datetime] NOT NULL,
   [EndDate] [datetime] NOT NULL
)


INSERT [dbo].[PricesHist] ([Product], [Price], [StartDate], [EndDate]) VALUES (N'Apples', 4.9, CAST(0x00009D2C00000000 AS DateTime), CAST(0x00009D2C00000000 AS DateTime))
INSERT [dbo].[PricesHist] ([Product], [Price], [StartDate], [EndDate]) VALUES (N'Apples', 4.9, CAST(0x00009D2D00000000 AS DateTime), CAST(0x00009D2D00000000 AS DateTime))
INSERT [dbo].[PricesHist] ([Product], [Price], [StartDate], [EndDate]) VALUES (N'Apples', 2.5, CAST(0x00009D2E00000000 AS DateTime), CAST(0x00009D2E00000000 AS DateTime))
INSERT [dbo].[PricesHist] ([Product], [Price], [StartDate], [EndDate]) VALUES (N'Apples', 4.9, CAST(0x00009D3000000000 AS DateTime), CAST(0x00009D3000000000 AS DateTime))
INSERT [dbo].[PricesHist] ([Product], [Price], [StartDate], [EndDate]) VALUES (N'Apples', 4.9, CAST(0x00009D3100000000 AS DateTime), CAST(0x00009D3100000000 AS DateTime))
INSERT [dbo].[PricesHist] ([Product], [Price], [StartDate], [EndDate]) VALUES (N'Apples', 4.9, CAST(0x00009D3400000000 AS DateTime), CAST(0x00009D3400000000 AS DateTime))
INSERT [dbo].[PricesHist] ([Product], [Price], [StartDate], [EndDate]) VALUES (N'Apples', 2.5, CAST(0x00009D3500000000 AS DateTime), CAST(0x00009D3500000000 AS DateTime))
INSERT [dbo].[PricesHist] ([Product], [Price], [StartDate], [EndDate]) VALUES (N'Apples', 4.9, CAST(0x00009D3600000000 AS DateTime), CAST(0x00009D3600000000 AS DateTime))
INSERT [dbo].[PricesHist] ([Product], [Price], [StartDate], [EndDate]) VALUES (N'Apples', 4.9, CAST(0x00009D3700000000 AS DateTime), CAST(0x00009D3700000000 AS DateTime))
INSERT [dbo].[PricesHist] ([Product], [Price], [StartDate], [EndDate]) VALUES (N'Apples', 4.9, CAST(0x00009D3800000000 AS DateTime), CAST(0x00009D3800000000 AS DateTime))
INSERT [dbo].[PricesHist] ([Product], [Price], [StartDate], [EndDate]) VALUES (N'Apples', 4.9, CAST(0x00009D3A00000000 AS DateTime), CAST(0x00009D3A00000000 AS DateTime))
INSERT [dbo].[PricesHist] ([Product], [Price], [StartDate], [EndDate]) VALUES (N'Apples', 4.9, CAST(0x00009D3B00000000 AS DateTime), CAST(0x00009D3B00000000 AS DateTime))
INSERT [dbo].[PricesHist] ([Product], [Price], [StartDate], [EndDate]) VALUES (N'Apples', 2.5, CAST(0x00009D3C00000000 AS DateTime), CAST(0x00009D3C00000000 AS DateTime))
INSERT [dbo].[PricesHist] ([Product], [Price], [StartDate], [EndDate]) VALUES (N'Apples', 4.9, CAST(0x00009D3D00000000 AS DateTime), CAST(0x00009D3D00000000 AS DateTime))
INSERT [dbo].[PricesHist] ([Product], [Price], [StartDate], [EndDate]) VALUES (N'Apples', 4.9, CAST(0x00009D3E00000000 AS DateTime), CAST(0x00009D3E00000000 AS DateTime))
INSERT [dbo].[PricesHist] ([Product], [Price], [StartDate], [EndDate]) VALUES (N'Apples', 4.9, CAST(0x00009D3F00000000 AS DateTime), CAST(0x00009D3F00000000 AS DateTime))
INSERT [dbo].[PricesHist] ([Product], [Price], [StartDate], [EndDate]) VALUES (N'Apples', 4.9, CAST(0x00009D4100000000 AS DateTime), CAST(0x00009D4100000000 AS DateTime))
INSERT [dbo].[PricesHist] ([Product], [Price], [StartDate], [EndDate]) VALUES (N'Apples', 4.9, CAST(0x00009D4200000000 AS DateTime), CAST(0x00009D4200000000 AS DateTime))
INSERT [dbo].[PricesHist] ([Product], [Price], [StartDate], [EndDate]) VALUES (N'Apples', 2.5, CAST(0x00009D4300000000 AS DateTime), CAST(0x00009D4300000000 AS DateTime))
INSERT [dbo].[PricesHist] ([Product], [Price], [StartDate], [EndDate]) VALUES (N'Apples', 4.9, CAST(0x00009D4400000000 AS DateTime), CAST(0x00009D4400000000 AS DateTime))
INSERT [dbo].[PricesHist] ([Product], [Price], [StartDate], [EndDate]) VALUES (N'Apples', 4.9, CAST(0x00009D4500000000 AS DateTime), CAST(0x00009D4500000000 AS DateTime))
INSERT [dbo].[PricesHist] ([Product], [Price], [StartDate], [EndDate]) VALUES (N'Apples', 4.9, CAST(0x00009D4600000000 AS DateTime), CAST(0x00009D4600000000 AS DateTime))
INSERT [dbo].[PricesHist] ([Product], [Price], [StartDate], [EndDate]) VALUES (N'Apples', 4.9, CAST(0x00009D4800000000 AS DateTime), CAST(0x00009D4800000000 AS DateTime))
INSERT [dbo].[PricesHist] ([Product], [Price], [StartDate], [EndDate]) VALUES (N'Apples', 2.5, CAST(0x00009D4A00000000 AS DateTime), CAST(0x00009D4A00000000 AS DateTime))

如您所见,苹果当月有两种价格。 4.90和2.50。 为了整理这个表格,我需要将此信息作为日期范围而不是当前每天的行。

我显然可以轻松地使用Min和Max聚合,但范围重叠,其他业务代码期望非重叠范围。 我也尝试使用self join和row_number()实现这一点,但没有太大的成功......

以下是我想要实现的输出:

Product | StartDate   |  EndDate    | Price
-------------------------------------------
Apples  | 01 Mar 2010 | 02 Mar 2010 | 4.90
Apples  | 03 Mar 2010 | 03 Mar 2010 | 2.50
Apples  | 05 Mar 2010 | 09 Mar 2010 | 4.90
Apples  | 10 Mar 2010 | 10 Mar 2010 | 2.50
Apples  | 11 Mar 2010 | 16 Mar 2010 | 4.90
Apples  | 17 Mar 2010 | 17 Mar 2010 | 2.50
Apples  | 18 Mar 2010 | 23 Mar 2010 | 4.90
Apples  | 24 Mar 2010 | 24 Mar 2010 | 2.50
Apples  | 25 Mar 2010 | 30 Mar 2010 | 4.90
Apples  | 31 Mar 2010 | 31 Mar 2010 | 2.50

要做到这一点,最好的方法是什么?

提前多多感谢,

1 个答案:

答案 0 :(得分:2)

这应该让你非常接近。一旦确定了处理缺失日期的方式,就可以适当调整它:

SELECT
    SD.Product,
    SD.Price,
    SD.StartDate,
    MAX(ED.EndDate) AS EndDate
FROM
    dbo.PricesHist SD
LEFT OUTER JOIN dbo.PricesHist ED ON
    ED.Product = SD.Product AND
    ED.Price = SD.Price
LEFT OUTER JOIN dbo.PricesHist LD ON
    LD.Product = SD.Product AND
    LD.Price <> SD.Price AND
    LD.EndDate < SD.StartDate
LEFT OUTER JOIN dbo.PricesHist LMD ON
    LMD.Product = SD.Product AND
    LMD.Price = SD.Price AND
    LMD.StartDate > ISNULL(LD.EndDate, '1900-01-01') AND
    LMD.StartDate < SD.StartDate
WHERE
    NOT EXISTS (SELECT * FROM dbo.PricesHist MD WHERE MD.Product = SD.Product AND MD.Price <> SD.Price AND MD.StartDate BETWEEN SD.StartDate AND ED.EndDate) AND
    LMD.Product IS NULL
GROUP BY
    SD.Product,
    SD.Price,
    SD.StartDate
ORDER BY
    SD.StartDate