检测是不是缺少一个月并使用选择语句(MSSQL)自动插入它们

Posted

技术标签:

【中文标题】检测是不是缺少一个月并使用选择语句(MSSQL)自动插入它们【英文标题】:Detect if a month is missing and insert them automatically with a select statement (MSSQL)检测是否缺少一个月并使用选择语句(MSSQL)自动插入它们 【发布时间】:2020-12-28 15:48:05 【问题描述】:

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

示例: 我的桌子是这样的:

语句之后应该是这样的:

【问题讨论】:

请用您正在运行的数据库标记您的问题:mysql、sql-server、oracle...? @GMB 对不起,我正在使用 MSSQL 创建一个包含所有日期的日历表(是的,这是一个常见的概念),然后在上面加入左键。或者创建一个数字表来动态创建您感兴趣的日历日期,然后加入。与用于填补空白的 CTE 相比,这两种方法都更简单、更有效。 【参考方案1】:

您需要一个递归的 CTE 来获取表中的所有年份(以及缺少的年份,如果有的话),另一个来获取所有月份编号 1-12。 这些 CTE 的 CROSS 连接将与表的 LEFT 连接一起连接,并最终过滤掉第一年/月之前和最后一年/月之后的行:

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。

【讨论】:

【参考方案2】:

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

一种方法是使用递归查询来生成表中最早日期和最晚日期之间的所有月初,然后使用left join 为表提供服务。

在 SQL Server 中:

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 ...

【讨论】:

以上是关于检测是不是缺少一个月并使用选择语句(MSSQL)自动插入它们的主要内容,如果未能解决你的问题,请参考以下文章

检查 switch 语句中是不是缺少默认情况

MSSQL里面建索引的问题

MS SQL 交叉连接性能评估

MSSQL语句学习(查询表的总记录数)

具有增量整数列的 MSSQL Select 语句...不是来自表

检测反序列化的对象是不是缺少 Json.NET 中 JsonConvert 类的字段