检测是不是缺少一个月并使用选择语句(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)自动插入它们的主要内容,如果未能解决你的问题,请参考以下文章