使用一个SQL查询查找所有缺少的数字

时间:2018-04-10 10:10:39

标签: sql sql-server

数据看起来像

[Month]     [Date]
---------------
201306      1
201306      2
201306      5
201306      6
201306      7
201307      1
201307      4
201307      6
201309      1
201309      2

如何按月找到所有缺失的日期?

以下是预期结果

[Month]     [Date]
---------------
201306      3
201306      4
201307      2
201307      3
201307      5

6 个答案:

答案 0 :(得分:2)

我认为这样做非常有效

declare @T table (yy int, dd int);
insert into @T values  
       (201306, 1)
     , (201306, 2)
     , (201306, 5)
     , (201306, 6)
     , (201306, 7)
     , (201307, 1)
     , (201307, 4)
     , (201307, 6)
     , (201309, 1)
     , (201309, 2);
with cte as 
( select yy, min(dd) + 1 as mn, max(dd) as mx 
  from @T 
  group by yy 
  having min(dd) + 1 < max(dd)
  union all 
  select c.yy, c.mn + 1, c.mx 
  from cte c
  where c.mn  + 1 < c.mx 
)
select yy, mn as dd 
from cte 
except 
select yy, dd 
from  @T t
order by yy, mn;

yy          dd
----------- -----------
201306      3
201306      4
201307      2
201307      3
201307      5

答案 1 :(得分:1)

您需要某种可能具有中间日期的lookup表,并使用cross joinleft join查找缺少的日期

首先 想法

;with cte as (
      select min(date) mdate, max(date) mxdate from table
      union all
      select mdate+1 as mdate,  mxdate
      from cte c
      where c.mdate < c.mxdate
)

select distinct t.Month, c.mdate 
from table t cross join (select mdate from cte) c
left join table t1 on t1.month = t.Month and t1.date = c.mdate
where t1.date is null

第二次 思想

;with cte as (
      select  month, min(date) over (partition by month) mdate, max(date) over (partition by month) mxdate 
      from sample t union all
      select month, mdate+1 as mdate,  mxdate
      from cte c
      where c.month = month and c.mdate < c.mxdate
)

select c.month, c.mdate 
from cte c left join sample t1 
           on t1.month = c.Month and t1.date = c.mdate
where t1.date is null
group by c.month, c.mdate

Demo

答案 2 :(得分:0)

考虑使用递归查询

with rndata as
(
  select row_number() over (partition by mon order by d) rn, * from data
), rcte as
(
  select mon, d, (select max(d) from data where data.mon = rndata.mon) max_d
  from rndata where rn = 1
  union all
  select rcte.mon, rcte.d + 1, rcte.max_d
  from rcte
  where rcte.d + 1 < max_d
)
select mon, d 
from rcte
where not exists (
  select 1
  from data 
  where rcte.mon = data.mon and
             rcte.d = data.d
)

dbfiddle demo

答案 3 :(得分:0)

考虑使用以下方法。

CREATE TABLE #Date([Month] int, [Date] int)

INSERT INTO #Date 
VALUES(201306, 1)
    ,(201306, 2)
    ,(201306, 5)
    ,(201306, 6)
    ,(201306, 7)
    ,(201307, 1)
    ,(201307, 4)
    ,(201307, 6)
    ,(201309, 1)
    ,(201309, 2)

;WITH CTE AS 
    (
        SELECT
        *,LEAD([Date]) OVER(ORDER BY [Month],[Date]) AS NextDate
        FROM #Date d
    )
    SELECT 
        d.[Month], m.Dt AS [Date]
    FROM CTE d
    CROSS APPLY(    SELECT v.Dt 
                    FROM 
                        (VALUES(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11)
                                ,(11),(12),(13),(14),(15),(16),(17),(18),(19),(20)
                                ,(21),(22),(23),(24),(25),(26),(27),(28),(29),(30),(31)
                        ) AS v(Dt)
                    WHERE v.Dt > d.Date AND v.Dt < d.NextDate
                ) m

这种方法的唯一限制是,它无法找到在第一次约会之前的失踪天数。

答案 4 :(得分:0)

这是一个快速但仍然很简单的解决方案:

1)使用sys.sysobjects作为计数表来获取每个月的所有日期 2)每个月的最小/最大范围,以保持范围内的间隙 3)加入计数和范围以获得每个月的预期日期,并将您的表格连接到数学现有日期 4)在WHERE条件下过滤仅缺少日期

declare @T table ([month] int, [date] int);
insert into @T values  
       (201306, 1)
     , (201306, 2)
     , (201306, 5)
     , (201306, 6)
     , (201306, 7)
     , (201307, 1)
     , (201307, 4)
     , (201307, 6)
     , (201309, 1)
     , (201309, 2);

with
n as (select top 31 ROW_NUMBER() over (order by id) n from sys.sysobjects),
r as (select [month], MIN([date]) dd1, MAX([date]) dd2 from @t group by [month])
select r.[month], n [date]
from r
join n on n between dd1 and dd2
left join @T t on n.N = t.[date] and r.[month] = t.[month]
where dd2<>dd1 and t.[date] is null
order by r.[month], n

答案 5 :(得分:-1)

你可以使用如下数字/ Tally表方法: See live demo

create table sample ([Month] int,      [Date] int)
insert into sample values 
(201306, 1)
,(201306, 2)
,(201306, 5)
,(201306, 6)
,(201306, 7)
,(201307, 1)
,(201307, 4)
,(201307, 6)
,(201309, 1)
,(201309, 2);

; with daysinmonth as 
(
    select * from
    (
        values
         (1,31),(2,28),(3,31),(4,30),(5,31),(6,30),
         (7,31),(8,31),(9,30),(10,31),(11,30),(12,31)
        ) v(m,d)

    )

select [month], dd 
from sample 
cross apply 
(
    select top 
        (
                select  d from
                daysinmonth 
                where m=cast( right(cast([Month] as varchar(6)),2) as int)
           )
    row_number() over ( order by (select null)) dd
    from
    sys.tables t1 cross join
    sys.tables t2 
  )  c
where [date]<>dd