Вставьте отсутствующие даты в существующую таблицу - PullRequest
0 голосов
/ 23 сентября 2018

У меня есть запрос, который находит пропущенные даты в таблице.

Запрос:

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

Выводколичество datetime, которые отсутствуют в FromDate to ToDate (которые существуют).Например, в первой строке вывода (выше) время, которое я хочу создать и вставить, будет '2018-09-20 14:45:00' и '2018-09-20 15:00:00' (все это 15-минутные интервалы)

Мне нужно понять, как я сейчас создаю новые даты и вставляю их в существующую таблицу.Я могу создать одну дату, но я не могу создать даты, когда между двумя значениями пропущено несколько значений.

TIA

SQL Fiddle

Ответы [ 3 ]

0 голосов
/ 23 сентября 2018

Если вы также хотите найти пропущенные даты в начале и в конце даты?

Тогда сравнение с сгенерированными датами должно быть надежным методом.

Такие даты могут быть созданы с помощью рекурсивного 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]
);

Тест можно найти здесь

А вот еще один запрос, который требует другого подхода:

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;

Вы можете проверить этот здесь

0 голосов
/ 24 сентября 2018

Основываясь на информации, которой я поделился, я сделал следующее, что делает то, что мне нужно.

Первая часть состоит в том, чтобы найти диапазоны дат, которые отсутствуют, путем поиска дат и дат, в которых отсутствуют датымежду ними, затем вставьте их в таблицу для аудита, но она будет содержать недостающие даты, которые я ищу:

;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, чтобы я мог контролировать оба аспекта, а именно: один для аудита и один для обратного заполнения.

0 голосов
/ 23 сентября 2018

Я не понимаю ваш запрос для расчета пропущенных значений.Ваш вопрос не имеет образцов данных или объяснения логики.Я почти уверен, что 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) в конец запроса.

...