T-SQL 小于 {date} 的分组在每次出现 date 时中断

T-SQL Grouping with LESS THAN {date} that breaks off on each occurrence of date

我正在努力使用 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 table,它显示客户的 OrderPoints 以及积分的到期日期。客户可能在此 table.

中有很多行

然后我们还有 CustomerOrderPointsUsed table,它显示已使用的积分以及客户使用它们的时间。

我正在尝试获取一组客户数据,这些数据将显示作为一组针对每个客户使用的 OrderPoints,但在 ExpiryDate 上分开。下图显示了我想要获得的分组结果的示例。

我们有使用递归方法 (RBAR) 开发的糟糕但有效的代码,但速度非常慢。我尝试了多种不同的基于 SET 的分组方法,但无法获得考虑到先前到期日期的最终 Less Than 分组。

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

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

有没有办法使用基于 SET 的 T-SQL 来实现这个?

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

首先,为每个 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