我有以下设置的数据:
ID | 日期 | 价值 |
---|---|---|
1 | 17.01.2023 | 不好 |
1 | 17.01.2023 | 好 |
1 | 18.01.2023 | 够了 |
1 | 17.01.2023 | 好 |
1 | 15.03.2023 | 不好 |
1 | 20.03.2023 | 好 |
2 | 10.01.2023 | 差 |
2 | 19.01.2023 | 够了 |
2 | 25.02.2023 | 差 |
2 | 11.03.2023 | 不好 |
3 | 20.12.2022 | 好 |
3 | 13.03.2023 | 好 |
3 | 29.03.2023 | 够了 |
目标是在每个月都有一个不丢失数据的SQL报告(下载到excel并创建图表)。例如,对于 id=1,我们有 1 月和 3 月的数据(2 月缺失)。在这种情况下,我们应该取前一个月的值。如果在一个月内我们有更多的值,那么我们取最后一个。所以预期的结果是:
ID | 日期 | 价值 |
---|---|---|
1 | 18.01.2023 | 够了 |
1 | 28.02.2023 | 够了 |
1 | 20.03.2023 | 好 |
2 | 19.01.2023 | 够了 |
2 | 25.02.2023 | 差 |
2 | 11.03.2023 | 差 |
3 | 20.12.2022 | 好 |
3 | 31.01.2023 | 好 |
3 | 28.02.2023 | 好 |
3 | 29.03.2023 | 够了 |
我们可以使用 EOMONTH() 来得到月末。重要的是,我们每个月都会有一个(最新的)值,以保持图表中数据的连续性。
你有什么想法吗?
提前谢谢你。
在数据库中,我们还有一张包含月份的表格。我试过如下:
SELECT
m.month
,t2.ID
,t2.value
FROM months
LEFT JOIN
(
SELECT
t.ID AS ID
,MONTH(t.Date) AS month
,t.value AS value
FROM table AS t
) AS t2 ON t2.month = m.month
但它作为一个 INNER JOIN 工作,我什至没有空飞蛾。
我喜欢使用的替代方法:
;WITH cte AS (
SELECT id,CONVERT(date, date, 104) AS date, value
FROM
(
VALUES (1, N'17.01.2023', N'Bad')
, (1, N'17.01.2023', N'Good')
, (1, N'18.01.2023', N'Enough')
, (1, N'17.01.2023', N'Good')
, (1, N'15.03.2023', N'Bad')
, (1, N'20.03.2023', N'Good')
, (2, N'10.01.2023', N'Bad')
, (2, N'19.01.2023', N'Enough')
, (2, N'25.02.2023', N'Bad')
, (2, N'11.03.2023', N'Bad')
, (3, N'20.12.2022', N'Good')
, (3, N'13.03.2023', N'Good')
, (3, N'29.03.2023', N'Enough')
) t (ID,Date,Value)
)
,cte2 AS (
SELECT *
, LAG(date) OVER(PARTITION BY id ORDER BY date) AS prevdate
, LAG(value) OVER(PARTITION BY id ORDER BY date) AS prevValue
, ROW_NUMBER() OVER(PARTITION BY id, EOMONTH(date) ORDER BY date DESC) AS sort
FROM cte
)
SELECT id, date, value
FROM cte2
WHERE sort = 1
UNION ALL
SELECT id, EOMONTH(DATEADD(MONTH, - x.counter, date)), prevValue
FROM cte2 c
CROSS APPLY (
SELECT TOP 40 row_number() OVER(ORDER BY @@spid) AS counter
FROM sys.objects so
CROSS APPLY sys.columns sc
) x
WHERE x.counter < DATEDIFF(month, c.prevdate, c.date)
ORDER BY id, date
我创建了一个“适当”行的联合(即每个月/年/id 的最后日期)和生成的行。
对于生成的行,我所做的是将上个月的值与当前日期进行比较,如果它大于 1,那么我会使用一个假系列表(您可以使用 GENERATE_SERIES 或任何其他可用的方法)来复制这些行,每个重复行的日期是 x 个月前。最后我在日期上应用 EOMONTH,以获得正确的日期。
一个选项可以使用以下步骤:
这些操作中的每一个都在不同的子查询中完成,结构如下:
WITH lastvalues AS (
SELECT DISTINCT ID,
FIRST_VALUE([Date]) OVER(PARTITION BY [ID], MONTH([Date])
ORDER BY [Date] DESC ) AS [Date],
FIRST_VALUE([Value]) OVER(PARTITION BY [ID], MONTH([Date])
ORDER BY [Date] DESC ) AS [Value],
MONTH([Date]) AS monthdate
FROM tab
), dates AS (
SELECT ids.ID,
EOMONTH(DATEADD(MONTH, value, '2022-12-01')) AS [Date],
MONTH(DATEADD(MONTH, value, '2022-12-01')) AS monthdate
FROM GENERATE_SERIES(0, 3)
CROSS JOIN (SELECT DISTINCT ID FROM tab) ids
), alldates AS (
SELECT dates.ID,
COALESCE(cte.[Date], dates.[Date]) AS [Date],
cte.[Value],
COUNT([Value]) OVER(PARTITION BY dates.ID ORDER BY dates.[Date]) AS parts
FROM dates
LEFT JOIN lastvalues cte
ON dates.monthdate = cte.monthdate AND dates.id = cte.id
)
SELECT ID,
[Date],
MAX([Value]) OVER(PARTITION BY ID, parts) AS [Value]
FROM alldates
输出:
ID | 日期 | 价值 |
---|---|---|
1 | 2022-12-31 | 空 |
1 | 2023-01-18 | 够了 |
1 | 2023-02-28 | 够了 |
1 | 2023-03-20 | 好 |
2 | 2022-12-31 | 空 |
2 | 2023-01-19 | 够了 |
2 | 2023-02-25 | 差 |
2 | 2023-03-11 | 差 |
3 | 2022-12-20 | 好 |
3 | 2023-01-31 | 好 |
3 | 2023-02-28 | 好 |
3 | 2023-03-29 | 够了 |
在这里查看演示.