我有一个表,该表继续客户 ID 并根据其结果记录分数。我遇到的问题是我需要按月显示分数,但会结转前几个月的分数,其中客户在过去的月份或当月没有完成测试。
例如,ClientID 1 在 2023 年 11 月、2024 年 1 月进行了测试,但在 2023 年 12 月、2024 年 2 月或 2024 年 3 月没有进行测试。因此查询需要将 2023 年 11 月的值带到 2023 年 12 月,然后再携带 1 月的值2024年进入2024年2月和2024年3月。
我真的不知道该怎么做。有人有什么想法吗?
Create Table #temp
(
ClientID int,
DateCreated datetime,
Score decimal(18,2)
)
insert into #temp
(
ClientID,
DateCreated,
Score
)
select
1,
'01 Nov 2023',
56
union all
select
1,
'15 Jan 2024',
90
union all
select
2,
'08 Dec 2023',
76
union all
select
2,
'25 Jan 2024',
98
union all
select
2,
'01 Mar 2024',
23
预期结果
客户端ID | 创建日期 | 分数 | 评论 |
---|---|---|---|
1 | 2023-11-01 00:00:00.000 | 56.00 | |
1 | 2023-12-01 00:00:00.000 | 56.00 | (自动生成) |
1 | 2024-01-15 00:00:00.000 | 90.00 | |
1 | 2024-02-01 00:00:00.000 | 90.00 | (自动生成) |
1 | 2024-03-01 00:00:00.000 | 90.00 | (自动生成) |
2 | 2023-12-08 00:00:00.000 | 76.00 | |
2 | 2024-01-25 00:00:00.000 | 98.00 | |
2 | 2024-02-01 00:00:00.000 | 98.00 | (自动生成) |
2 | 2024-03-01 00:00:00.000 | 23.00 | (自动生成) |
这不像这个问题:获取最后一个非空值?因为我没有日期的空值。原始日期实际上并不存在。这个问题也有四年的历史了,接受的答案提到有更好的方法来做到这一点,新功能即将到来。
看起来你的问题有两个部分:
第一部分可以通过生成一个列表或所有感兴趣的月份(递归 CTE 是一种技术)加上所有客户端 ID 的不同列表来完成。然后,您可以交叉连接两个源并应用
WHERE NOT EXISTS(...)
条件来排除数据已存在的源。
然后,您可以使用
CROSS APPLY(SELECT TOP 1 ... ORDER BY ...)
模式查找“最佳匹配”(最近的先前)数据行以检索该分数。这将为您提供所需的生成行。
然后可以使用
UNION ALL
将其与您的原始数据相结合以获得最终结果。
完成的查询将类似于:
WITH Months AS (
SELECT DATEADD(month, DATEDIFF(month, 0,
(SELECT MIN(T.DateCreated) FROM #Temp T)
), 0) AS Month -- Tricky, equivalent of DATETRUNC(month, ...)
UNION ALL
SELECT DATEADD(month, 1, M.Month) AS Month
FROM Months M
WHERE DATEADD(month, 1, M.Month) < GETDATE()
),
Clients AS (
SELECT DISTINCT T.ClientId
FROM #Temp T
)
SELECT T.ClientID, M.Month AS DateCreated, T.Score, Comments = '(auto-generated)'
FROM Months M
CROSS APPLY Clients C
CROSS APPLY (
SELECT TOP 1 *
FROM #Temp T
WHERE T.ClientId = C.ClientId
AND T.DateCreated < M.Month -- Prior
ORDER BY T.DateCreated DESC -- Most Recent
) T
WHERE NOT EXISTS (
-- Row does not exists for current month and client
SELECT *
FROM #Temp T
WHERE T.ClientId = C.ClientId
AND T.DateCreated >= M.Month
AND T.DateCreated < DATEADD(month, 1, M.Month)
)
UNION ALL
SELECT T.ClientID, T.DateCreated, T.Score, Comments = ''
FROM #Temp T
ORDER BY CLientId, DateCreated
以上适用于 SQL Server 2017。如果我们使用更高版本的 SQL Server,我们可以使用
DATE_TRUNC()
函数,或许还可以使用 GENERATE_SERIES()
的形式来简化月份范围 CTE。
结果:
客户端ID | 创建日期 | 分数 | 评论 |
---|---|---|---|
1 | 2023-11-01 00:00:00.000 | 56.00 | |
1 | 2023-12-01 00:00:00.000 | 56.00 | (自动生成) |
1 | 2024-01-15 00:00:00.000 | 90.00 | |
1 | 2024-02-01 00:00:00.000 | 90.00 | (自动生成) |
1 | 2024-03-01 00:00:00.000 | 90.00 | (自动生成) |
2 | 2023-12-08 00:00:00.000 | 76.00 | |
2 | 2024-01-25 00:00:00.000 | 98.00 | |
2 | 2024-02-01 00:00:00.000 | 98.00 | (自动生成) |
2 | 2024-03-01 00:00:00.000 | 23.00 |
请参阅 this db<>fiddle(适用于 SQL Server 2017)进行演示。