将遗漏的日期插入现有表格

时间:2018-09-23 11:23:41

标签: sql sql-server common-table-expression

我有一个查询,用于查找表中缺少的日期。

查询为:

;WITH NullGaps AS
(
    SELECT 
        ROW_NUMBER() OVER (ORDER BY ChannelName, ReadingDate) AS ID,
        SerialNumber, ReadingDate, ChannelName, uid
    FROM 
        [UriData]   
)   
SELECT 
    (DATEDIFF(MINUTE, g1.ReadingDate , g2.ReadingDate) / 15) -1 AS 'MissingCount',
    g1.ReadingDate AS 'FromDate', g2.ReadingDate AS 'ToDate'                
FROM 
    NullGaps g1
INNER JOIN 
    NullGaps g2 ON g1.ID = (g2.ID - 1)
WHERE 
    DATEADD(MINUTE, 15, g1.ReadingDate) < g2.ReadingDate    

输出为:

--------------------------------------------------------------
| MissingCount |      FromDate         |      ToDate         |
--------------------------------------------------------------
|    2         |  2018-09-20 14:30:00  | 2018-09-20 15:15:00 |
|    1         |  2018-09-20 15:30:00  | 2018-09-20 16:00:00 |
|    1         |  2018-09-20 20:30:00  | 2018-09-20 21:00:00 |
-------------------------------------------------------------- 

输出是从FromDate到ToDate(两者都存在)缺少的日期时间数。例如,在输出的第一行(上方)中,我要创建和插入的时间将是'2018-09-20 14:45:00''2018-09-20 15:00:00'(它们都是15分钟的间隔)

我需要了解如何现在创建新日期并将其插入到现有表中。我可以创建一个日期,但是不能创建两次之间存在多个缺失值的日期。

TIA

SQL Fiddle

3 个答案:

答案 0 :(得分:1)

我不理解您的查询来计算缺失值。您的问题没有示例数据或没有逻辑解释。我很确定lag()会简单得多。

但是考虑到您的查询(或其他查询),扩展数据的一种方法是使用递归CTE:

with missing as (<your query here>)
     cte as (
      select dateadd(minute, 15, fromdate) as dte, missingcount - 1 as missingcount
      from missing
      union all
      select dateadd(minute, 15, dte), missingcount - 1
      from cte
      where missingcount > 0
     )
select *
from cte;

如果一行中缺少的时间超过100次,则在查询的末尾添加option (maxrecursion 0)

答案 1 :(得分:1)

是否还想在日期的开始和结束时查找缺少的日期时间?

然后将其与生成的日期时间进行比较应该是一种有效的方法。

此类日期可以通过递归CTE生成。

然后,您可以将数据连接到递归CTE并选择缺少的数据。
或使用NOT EXISTS

例如:

WITH RCTE AS
(
  select [SerialNumber], [ChannelName], 0 as Lvl, cast(cast([ReadingDate] as date) as datetime) as ReadingDate
  from [UriData]
  group by SerialNumber, [ChannelName], cast([ReadingDate] as date)

  union all

  select [SerialNumber], [ChannelName], Lvl + 1, DATEADD(MINUTE,15,[ReadingDate])
  from RCTE
  where cast([ReadingDate] as date) = cast(DATEADD(MINUTE,15,[ReadingDate]) as date)
)
SELECT [SerialNumber], [ChannelName], [ReadingDate] AS FromDate
FROM RCTE r
WHERE NOT EXISTS
(
  select 1
  from [UriData] t
  where t.[SerialNumber] = r.[SerialNumber]
    and t.[ChannelName] = r.[ChannelName]
    and t.[ReadingDate] = r.[ReadingDate]
);

可以找到测试here

这是另一个使用不同approuch的查询:

WITH CTE AS
(
  SELECT SerialNumber, ChannelName, ReadingDate,
   LAG(ReadingDate) OVER (PARTITION BY SerialNumber, ChannelName ORDER BY ReadingDate) AS prevReadingDate
  FROM [UriData] 
)
, RCTE AS
(
  select SerialNumber, ChannelName, 0 as Lvl, 
   prevReadingDate AS ReadingDate, 
   prevReadingDate AS MinReadingDate,
   ReadingDate AS MaxReadingDate
  from CTE
  where DATEDIFF(MINUTE, prevReadingDate, ReadingDate) > 15

  union all

  select SerialNumber, ChannelName, Lvl + 1, 
   DATEADD(MINUTE,15,ReadingDate),
   MinReadingDate,
   MaxReadingDate
  from RCTE
  where ReadingDate < DATEADD(MINUTE,-15,MaxReadingDate)
)
select SerialNumber, ChannelName, 
 ReadingDate AS FromDate, 
 DATEADD(MINUTE,15,ReadingDate) AS ToDate, 
 dense_rank() over (partition by SerialNumber, ChannelName order by MinReadingDate) as GapRank,
 (DATEDIFF(MINUTE, MinReadingDate, MaxReadingDate) / 15)  AS TotalMissingQuarterGaps
from RCTE
where Lvl > 0 AND MinReadingDate < MaxReadingDate
ORDER BY SerialNumber, ChannelName, MinReadingDate;

您可以测试一个here

答案 2 :(得分:0)

根据与我分享的信息,我做了以下我所需的事情。

第一部分是通过查找日期和日期之间缺少日期的日期来查找缺少的日期范围,然后将它们插入表中进行审核,但是它将保留我要查找的日期:

;WITH NullGaps AS(
        SELECT ROW_NUMBER() OVER (ORDER BY ChannelName, ReadingDate) AS ID,SerialNumber, ReadingDate, ChannelName, uid
        FROM [Staging].[UriData]
    ) 
        INSERT INTO [Staging].[MissingDates]
        SELECT (DATEDIFF(MINUTE, g1.ReadingDate , g2.ReadingDate) / 15) -1 AS 'MissingCount', 
                g1.ChannelName, 
                g1.SerialNumber,
                g1.ReadingDate AS FromDate,
                g2.ReadingDate AS ToDate              
        FROM NullGaps g1
            INNER JOIN NullGaps g2 
            ON g1.ID = (g2.ID - 1)
        WHERE DATEADD(MINUTE, 15, g1.ReadingDate) < g2.ReadingDate 
            AND g1.ChannelName IN (SELECT ChannelName FROM staging.ActiveChannels) 
            AND NOT EXISTS(
                    SELECT 1 FROM [Staging].[MissingDates] m 
                    WHERE m.Channel = g1.ChannelName
                        AND m.Serial = g1.SerialNumber
                        AND m.FromDate = g1.ReadingDate
                        AND m.ToDate = g2.ReadingDate
            )

现在有了要查找的范围,我现在可以创建缺失的日期并将其插入保存真实数据的表中。

;WITH MissingDateTime AS(
            SELECT DATEADD(MINUTE, 15, FromDate) AS dte, MissingCount -1 AS MissingCount, Serial, Channel
                FROM [Staging].[MissingDates]
            UNION ALL
            SELECT DATEADD(MINUTE, 15, dte), MissingCount - 1, Serial, Channel
                FROM MissingDateTime
                WHERE MissingCount > 0
        ) -- END CTE

        INSERT INTO [Staging].[UriData]
        SELECT NEWID(), Serial, Channel, '999', '0', dte, CURRENT_TIMESTAMP, 0,1,0 FROM MissingDateTime m
        WHERE NOT EXISTS(
            SELECT 1 FROM [Staging].[UriData] u
                WHERE u.ChannelName = m.Channel
                AND u.SerialNumber = m.Serial
                AND u.ReadingDate = m.dte
            ) -- END SELECT

我确信您可以对此进行改进。此解决方案仅查找缺少的日期,并允许我仅用缺少的日期回填数据表。如果以后需要将其他设备用于不同的间隔,我也可以更改间隔。我将查询放在两个分散的SPROC中,以便可以同时控制两个方面:一个用于审计,一个用于回填。