少于{date}的T-SQL分组会在每次出现日期时中断

时间:2018-08-21 14:56:51

标签: tsql group-by sql-server-2008-r2

我正在努力使用LESS THAN创建分组,该分组在上一行的每个日期中断。我创建了一个人为的示例来解释数据以及我想要得到的结果:

CREATE TABLE [dbo].[CustomerOrderPoints](
[CustomerID] [int] NOT NULL,
[OrderPoints] [int] NOT NULL,
[OrderPointsExpiry] [date] NOT NULL
) ON [PRIMARY]
GO

CREATE TABLE [dbo].[CustomerOrderPointsUsed](
[CustomerID] [int] NOT NULL,
[OrderPointsUsed] [int] NOT NULL,
[OrderPointsUseDate] [date] NOT NULL
) ON [PRIMARY]
GO

INSERT [dbo].[CustomerOrderPoints] ([CustomerID], [OrderPoints], [OrderPointsExpiry]) VALUES (10, 200, CAST(N'2018-03-18' AS Date))
GO
INSERT [dbo].[CustomerOrderPoints] ([CustomerID], [OrderPoints], [OrderPointsExpiry]) VALUES (10, 100, CAST(N'2018-04-18' AS Date))
GO
INSERT [dbo].[CustomerOrderPoints] ([CustomerID], [OrderPoints], [OrderPointsExpiry]) VALUES (20, 120, CAST(N'2018-05-10' AS Date))
GO
INSERT [dbo].[CustomerOrderPoints] ([CustomerID], [OrderPoints], [OrderPointsExpiry]) VALUES (30, 75, CAST(N'2018-02-10' AS Date))
GO
INSERT [dbo].[CustomerOrderPoints] ([CustomerID], [OrderPoints], [OrderPointsExpiry]) VALUES (30, 60, CAST(N'2018-04-24' AS Date))
GO
INSERT [dbo].[CustomerOrderPoints] ([CustomerID], [OrderPoints], [OrderPointsExpiry]) VALUES (30, 90, CAST(N'2018-06-25' AS Date))
GO
INSERT [dbo].[CustomerOrderPoints] ([CustomerID], [OrderPoints], [OrderPointsExpiry]) VALUES (40, 100, CAST(N'2018-06-13' AS Date))
GO
INSERT [dbo].[CustomerOrderPointsUsed] ([CustomerID], [OrderPointsUsed], [OrderPointsUseDate]) VALUES (10, 15, CAST(N'2018-02-10' AS Date))
GO
INSERT [dbo].[CustomerOrderPointsUsed] ([CustomerID], [OrderPointsUsed], [OrderPointsUseDate]) VALUES (10, 30, CAST(N'2018-02-17' AS Date))
GO
INSERT [dbo].[CustomerOrderPointsUsed] ([CustomerID], [OrderPointsUsed], [OrderPointsUseDate]) VALUES (10, 25, CAST(N'2018-03-16' AS Date))
GO
INSERT [dbo].[CustomerOrderPointsUsed] ([CustomerID], [OrderPointsUsed], [OrderPointsUseDate]) VALUES (10, 45, CAST(N'2018-04-10' AS Date))
GO
INSERT [dbo].[CustomerOrderPointsUsed] ([CustomerID], [OrderPointsUsed], [OrderPointsUseDate]) VALUES (20, 10, CAST(N'2018-02-08' AS Date))
GO
INSERT [dbo].[CustomerOrderPointsUsed] ([CustomerID], [OrderPointsUsed], [OrderPointsUseDate]) VALUES (20, 70, CAST(N'2018-04-29' AS Date))
GO
INSERT [dbo].[CustomerOrderPointsUsed] ([CustomerID], [OrderPointsUsed], [OrderPointsUseDate]) VALUES (20, 25, CAST(N'2018-05-29' AS Date))
GO
INSERT [dbo].[CustomerOrderPointsUsed] ([CustomerID], [OrderPointsUsed], [OrderPointsUseDate]) VALUES (30, 60, CAST(N'2018-02-05' AS Date))
GO
INSERT [dbo].[CustomerOrderPointsUsed] ([CustomerID], [OrderPointsUsed], [OrderPointsUseDate]) VALUES (30, 30, CAST(N'2018-03-13' AS Date))
GO
INSERT [dbo].[CustomerOrderPointsUsed] ([CustomerID], [OrderPointsUsed], [OrderPointsUseDate]) VALUES (40, 120, CAST(N'2018-06-10' AS Date))

客户获得的积分已过期。我们有一个CustomerOrderPoints表,该表显示了客户的OrderPoints以及该点的过期日期。客户在此表中可能有很多行。

然后我们还有CustomerOrderPointsUsed表,该表显示了已使用的点以及客户使用它们的时间。

我正在尝试将一组Customer数据显示为每个客户使用Group的OrderPoints,但在ExpiryDate上将其分开。下图显示了我要获取的分组结果的示例。

Example layout of results

我们做的不好,但是使用递归方法(RBAR)开发的工作代码非常慢。我尝试了多种不同的基于SET的分组方法,但是无法获得考虑到之前的到期日期的最终的小于分组。

此数据库位于SQL Server 2008R2上。理想情况下,我正在寻找一种可以与SQL Server 2008R2一起使用的解决方案,但是将欢迎使用更高版本的选项,因为我们可能需要移动特定的DB才能解决此问题。

我尝试将RanK,DenseRank和RowNumber(对于更高版本)和LAG结合使用,但无法获得任何可以构建的工作。

有没有一种方法可以使用基于SET的T-SQL来实现?

1 个答案:

答案 0 :(得分:0)

首先-这忽略了我在上面的注释中提出的问题,只是将所有行分配到使用日期之前或之后的到期日期。如果您需要在多个到期日期之间分配一种用途,则需要重新考虑这一点

首先,为每个PointsUsed行分配一个到期日期。这是通过将所有截止日期在UseDate或之后的OrderPoints行联接起来,然后采用最短的日期来完成的。

然后,第二个查询报告所有OrderPoints行,并在分配的到期日期前加入第一个查询,其中包含所需的所有数据。

WITH allocatedPoints as 
(
    Select U.CustomerID, U.OrderPointsUsed, MIN(P.OrderPointsExpiry) as OrderPointsExpiry
    from CustomerOrderPointsUsed U 
    inner join CustomerOrderPoints P on P.CustomerID = U.CustomerID and P.OrderPointsExpiry >= U.OrderPointsUseDate
GROUP BY U.CustomerID, U.OrderPointsUseDate, U.OrderPointsUsed
)

Select P.CustomerID, P.OrderPoints, P.OrderPointsExpiry, 
    ISNULL(SUM(AP.OrderPointsUsed), 0) as used,
    P.OrderPoints - ISNULL(SUM(AP.OrderPointsUsed), 0) as remaining
from CustomerOrderPoints P
left outer join allocatedPoints AP on AP.CustomerID = P.CustomerID and AP.OrderPointsExpiry = P.OrderPointsExpiry
GROUP BY P.CustomerID, P.OrderPoints, P.OrderPointsExpiry