Создание моментальных снимков в данных TimeSeries - PullRequest
1 голос
/ 01 ноября 2019

У меня есть ежедневный снимок данных. Теперь я хочу сделать из него данные временного ряда, используя SQL. Я пробовал какой-то метод, но он имеет определенные ограничения.

Пример данных:

enter image description here

Ожидаемый результат:

enter image description here

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

SELECT [name], [value],
[date] as [start],
DATEADD(DAY, -1, LEAD([date], 1) OVER(PARTITION BY [name] ORDER BY [date])) AS [end]
FROM (
    SELECT *,
    RANK() OVER(Partition by [name], [rnk] ORDER BY [date]) as row_num 
    FROM(
        SELECT [name], [value], [date],
        DENSE_RANK() OVER(Partition by [name] ORDER BY [value]) AS rnk
        FROM sample_data
    ) AS T
) AS TT
WHERE row_num = 1

Результат сверху SQL:

enter image description here

Любая помощь высоко ценится!

Ответы [ 3 ]

2 голосов
/ 01 ноября 2019

Это проблема gaps-and-islands. Вы можете попробовать это.

SELECT Name, Value, MIN([Date]) Start, MAX([Date]) [End] FROM (
    SELECT *, 
        ROW_NUMBER() OVER(PARTITION BY Name  ORDER BY [Date]) 
        - ROW_NUMBER() OVER(PARTITION BY Name, Value ORDER BY [Date]) AS GRP
    FROM sample_data
) T 
GROUP BY Name, Value, GRP
ORDER BY Name, Start
1 голос
/ 01 ноября 2019

Это решение для алгоритмов, известных как острова и пробелы.

;WITH [Islands] AS 
(
    SELECT 'A' AS [Name], 0 AS [Value], CAST('2019-10-24' AS DATE) AS [Date] UNION
    SELECT 'A' AS [Name], 0 AS [Value], CAST('2019-10-25' AS DATE) AS [Date] UNION
    SELECT 'A' AS [Name], 0 AS [Value], CAST('2019-10-26' AS DATE) AS [Date] UNION

    SELECT 'A' AS [Name], 1 AS [Value], CAST('2019-10-27' AS DATE) AS [Date] UNION
    SELECT 'A' AS [Name], 1 AS [Value], CAST('2019-10-28' AS DATE) AS [Date] UNION
    SELECT 'A' AS [Name], 1 AS [Value], CAST('2019-10-29' AS DATE) AS [Date] UNION

    SELECT 'A' AS [Name], 0 AS [Value], CAST('2019-10-30' AS DATE) AS [Date] UNION
    SELECT 'A' AS [Name], 0 AS [Value], CAST('2019-10-31' AS DATE) AS [Date]
)
, [IslandGroups] AS 
(
    SELECT
        *
        ,DATEDIFF(DAY, '1900-01-01', [Date]) AS [DifferenceInDays]
        ,ROW_NUMBER() OVER (ORDER BY [Name], [Value]) AS [RowNumber]
        ,DATEDIFF(DAY, '1900-01-01', [Date]) - ROW_NUMBER() OVER (ORDER BY [Name], [Value]) AS [IslandGroup]
    FROM
        [Islands]
)
SELECT
    [Name]
    ,[Value]
    ,MIN([Date]) AS [starting_date]
    ,MAX([Date]) AS [starting_date]
FROM
    [IslandGroups]
GROUP BY
    [Name]
    ,[Value]
    ,[IslandGroup]
ORDER BY
    [Name]
    ,MIN([Date])

Вот как это работает. Алгоритм работает путем вычитания функции ранжирования, в данном случае ROW_NUMBER () из разницы в днях между двумя датами. Если вы запустите это, вы увидите, что столбец RowNumber увеличивается, как и DifferenceInDays.

... removed for brevity
, [IslandGroups] AS 
(
    SELECT
        *
        ,DATEDIFF(DAY, '1900-01-01', [Date]) AS [DifferenceInDays]
        ,ROW_NUMBER() OVER (ORDER BY [Name], [Value]) AS [RowNumber]
        ,DATEDIFF(DAY, '1900-01-01', [Date]) - ROW_NUMBER() OVER (ORDER BY [Name], [Value]) AS [IslandGroup]
    FROM
        [Islands]
)
SELECT
    *
FROM
    [IslandGroups]

В результате:

A   0   2019-10-24  43760   1   43759 <- First in the series
A   0   2019-10-25  43761   2   43759
A   0   2019-10-26  43762   3   43759
A   0   2019-10-30  43766   4   43762 <- Next set
A   0   2019-10-31  43767   5   43762
A   1   2019-10-27  43763   6   43757 <- Next set
A   1   2019-10-28  43764   7   43757
A   1   2019-10-29  43765   8   43757

Затем вы можете GROUP BY общей группировки островов иполучить MIN () и MAX () [Date] из одной группы.

1 голос
/ 01 ноября 2019

SQL Fiddle

Настройка схемы MS SQL Server 2017 :

create table sample_data(Name varchar(max), Value int , Date date)
insert into sample_data(Name,Value,Date)values('A',0,'2019-10-24')
insert into sample_data(Name,Value,Date)values('A',0,'2019-10-25')
insert into sample_data(Name,Value,Date)values('A',0,'2019-10-26')
insert into sample_data(Name,Value,Date)values('A',1,'2019-10-27')
insert into sample_data(Name,Value,Date)values('A',1,'2019-10-28')
insert into sample_data(Name,Value,Date)values('A',1,'2019-10-29')
insert into sample_data(Name,Value,Date)values('A',0,'2019-10-30')
insert into sample_data(Name,Value,Date)values('A',0,'2019-10-31')

Запрос 1 :

WITH CTE AS (
  SELECT *, 
        ROW_NUMBER() OVER(PARTITION BY Name  ORDER BY Date ) 
        - ROW_NUMBER() OVER(PARTITION BY Name,Value ORDER BY Date  ) AS Interval
    FROM sample_data
  )


SELECT Name, Value, MIN(Date) Starting_Date, MAX(Date) Ending_Date FROM CTE    
GROUP BY Name, Value, Interval
Order BY Name,Starting_Date

Результаты :

| Name | Value | Starting_Date | Ending_Date |
|------|-------|---------------|-------------|
|    A |     0 |    2019-10-24 |  2019-10-26 |
|    A |     1 |    2019-10-27 |  2019-10-29 |
|    A |     0 |    2019-10-30 |  2019-10-31 |
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...