检测是否缺少月份并使用 select 语句自动插入它们 (MSSQL)

Detect if a month is missing and insert them automatically with a select statement (MSSQL)

我正在尝试编写一个 select 语句来检测月份是否不存在并自动插入值为 0 的月份。它应该插入从第一个条目到最后一个条目的所有缺失月份。

示例: 我的 table 看起来像这样:

声明后应该是这样的:

您需要一个递归 CTE 来获取 table 中的所有年份(以及缺失的年份,如果有的话),另一个获取所有月份编号 1-12。
这些 CTE 的 CROSS 连接将与 table 的 LEFT 连接连接并最终过滤,以便第一个 year/month 之前的行和最后一个 year/month 被排除在外:

WITH
  limits AS (
    SELECT MIN(year) min_year, -- min year in the table
           MAX(year) max_year, -- max year in the table
           MIN(DATEFROMPARTS(year, monthnum, 1)) min_date, -- min date in the table 
           MAX(DATEFROMPARTS(year, monthnum, 1)) max_date  -- max date in the table
    FROM tablename
   ),
  years(year) AS ( -- recursive CTE to get all the years of the table (and the missing ones if any)
    SELECT min_year FROM limits
    UNION ALL
    SELECT year + 1 
    FROM years
    WHERE year < (SELECT max_year FROM limits)
  ),  
  months(monthnum) AS ( -- recursive CTE to get all the month numbers 1-12
    SELECT 1 
    UNION ALL
    SELECT monthnum + 1
    FROM months
    WHERE monthnum < 12
  )
SELECT y.year, m.monthnum,
       DATENAME(MONTH, DATEFROMPARTS(y.year, m.monthnum, 1)) month,
       COALESCE(value, 0) value
FROM months m CROSS JOIN years y
LEFT JOIN tablename t 
ON t.year = y.year AND t.monthnum = m.monthnum
WHERE DATEFROMPARTS(y.year, m.monthnum, 1) 
      BETWEEN (SELECT min_date FROM limits) AND (SELECT max_date FROM limits)
ORDER BY y.year, m.monthnum 

参见demo

您不应将日期组件存储在两个单独的列中;相反,您应该只有一列,具有适当的 date 类数据类型。

一种方法是使用递归查询生成 table 中最早和最晚日期之间的所有月份开始,然后用 left join 填充 table。

在 SQL 服务器中:

with cte as (
    select min(datefromparts(year, monthnum, 1)) as dt,
        max(datefromparts(year, monthnum, 1)) as dt_max
    from mytable
    union all
    select dateadd(month, 1, dt) 
    from cte
    where dt < dt_max
)
select c.dt, coalesce(t.value, 0) as value
from cte c
left join mytable t on datefromparts(t.year, t.month, 1) = c.dt

如果您的数据分布超过 100 个月,您需要在查询末尾添加 option(maxrecursion 0)

如果您愿意,可以在最后 select 中提取日期组件:

select 
    year(c.dt) as yr, 
    month(c.dt) as monthnum, 
    datename(month, c.dt) as monthname,
    coalesce(t.value, 0) as value
from ...