扩展 SELECT 中缺失的数据

问题描述 投票:0回答:2

我有以下设置的数据:

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 工作,我什至没有空飞蛾。

sql sql-server select missing-data gaps-and-islands
2个回答
1
投票

我喜欢使用的替代方法:

;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,以获得正确的日期。


0
投票

一个选项可以使用以下步骤:

  • 为您的每个 ID 提取最后的 [Date]、[Value]
  • 生成您的 ID 与部分日历表的组合
  • 在匹配的 id 和月份日期上将日历表与 lastvalues 表左连接
  • 通过重建分区解决剩余的间隙和孤岛问题(一个非空值+后续空值分配给非空值)

这些操作中的每一个都在不同的子查询中完成,结构如下:

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 够了

在这里查看演示.

© www.soinside.com 2019 - 2024. All rights reserved.