Получить количество статусов по дате, но только количество непрерывных строк - PullRequest
0 голосов
/ 11 сентября 2018

У меня есть эти данные:

ID  Name        Status  Date
1   Machine1    Active  2018-01-01
2   Machine2    Fault   2018-01-01
3   Machine3    Active  2018-01-01
4   Machine1    Fault   2018-01-02
5   Machine2    Active  2018-01-02
6   Machine3    Active  2018-01-02
7   Machine2    Active  2018-01-03
8   Machine1    Fault   2018-01-03
9   Machine2    Active  2018-01-04
10  Machine1    Fault   2018-01-04
11  Machine3    Active  2018-01-06

ВХОД

и я хочу эти данные в выводе

ОЖИДАЕМЫЙ ВЫХОД

Name           Last Status  Count
Machine1         Fault       3  
Machine2         Active      3  
Machine3         Active      1       Because Date is not Continuous

* Количество: последний номер статуса в непрерывной истории

Ответы [ 2 ]

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

Я считаю, что это так просто:

WITH cte1 AS (
    SELECT
        Name,
        Status,
        DATEADD(DAY, ROW_NUMBER() OVER (PARTITION BY Name, Status ORDER BY Date DESC) - 1, Date) AS GroupingDate
    FROM testdata
), cte2 AS (
    SELECT
        Name,
        Status,
        RANK() OVER (PARTITION BY Name ORDER BY GroupingDate DESC) AS GroupingNumber
    FROM cte1
)
SELECT Name, Status AS LastStatus, COUNT(*) AS LastStatusCount
FROM cte2
WHERE GroupingNumber = 1
GROUP BY Name, Status
ORDER BY Name

Результат и DBFiddle :

| Name     | LastStatus | LastStatusCount |
|----------|------------|-----------------|
| Machine1 | Fault      | 3               |
| Machine2 | Active     | 3               |
| Machine3 | Active     | 1               |

Чтобы понять, как это работает, посмотрите на промежуточные значения, сгенерированные CTE:

| Name     | Status | Date                | RowNumber | GroupingDate        | GroupingNumber |
|----------|--------|---------------------|-----------|---------------------|----------------|
| Machine1 | Fault  | 04/01/2018 00:00:00 | 1         | 04/01/2018 00:00:00 | 1              |
| Machine1 | Fault  | 03/01/2018 00:00:00 | 2         | 04/01/2018 00:00:00 | 1              |
| Machine1 | Fault  | 02/01/2018 00:00:00 | 3         | 04/01/2018 00:00:00 | 1              |
| Machine1 | Active | 01/01/2018 00:00:00 | 1         | 01/01/2018 00:00:00 | 4              |
| Machine2 | Active | 04/01/2018 00:00:00 | 1         | 04/01/2018 00:00:00 | 1              |
| Machine2 | Active | 03/01/2018 00:00:00 | 2         | 04/01/2018 00:00:00 | 1              |
| Machine2 | Active | 02/01/2018 00:00:00 | 3         | 04/01/2018 00:00:00 | 1              |
| Machine2 | Fault  | 01/01/2018 00:00:00 | 1         | 01/01/2018 00:00:00 | 4              |
| Machine3 | Active | 06/01/2018 00:00:00 | 1         | 06/01/2018 00:00:00 | 1              |
| Machine3 | Active | 02/01/2018 00:00:00 | 2         | 03/01/2018 00:00:00 | 2              |
| Machine3 | Active | 01/01/2018 00:00:00 | 3         | 03/01/2018 00:00:00 | 2              |

Хитрость в том, что если два числа являются смежными, то вычитание из них смежных чисел приведет к одинаковому значению. Например. если у нас 5, 6, 8, 9, то вычитание 1, 2, 3, 4 в этом порядке даст 4, 4, 5, 5.

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

Я думаю, что это будет работать, хотя SQLFiddle в данный момент подходит, поэтому я не могу проверить:

SELECT [Name], [Status], ct as [Count]
FROM (
 SELECT 
  [name], 
  [status], 
  [date],
  1 + (SUM( grp ) OVER (PARTITION BY [name], [status] ORDER BY [date] ROWS BETWEEN 1 PRECEDING AND 0 FOLLOWING ) * grp) ct,
  row_number() over(partition by [name] order by [date] desc) rn
 FROM
 (
  SELECT *, CASE WHEN LAG([Date]) OVER(PARTITION BY [name], [status] ORDER BY [date] ) = DATEADD(day, -1, [date]) THEN 1 ELSE 0 END grp
  FROM t
 ) x
) y
WHERE
  rn = 1

Сначала он использует LAG для просмотра текущей строки и предыдущей строки (группирование данных по имени и статусу машины, упорядочение данных по дате), и если текущая дата на 1 день отличается от предыдущей даты, он записывает 1 еще 0

Эти 1 и нули суммируются в виде промежуточного итога, сбрасываются при изменении имени машины или состояния (разделение суммы () на ())

Также мы хотим рассматривать данные только с точки зрения имени машины, и нам нужна только самая последняя запись с каждой машины, поэтому мы разбиваем по имени машины и считаем в порядке убывания даты, затем просто выбираем (с предложение where) строки с номерами 1 для каждой машины

На самом деле это имеет гораздо больше смысла, если вы выполняете запросы отдельно, как это

Рассчитать «текущий отчет, следующий за предыдущим отчетом, для данного состояния и машины» 1 = да, 0 = нет:

SELECT *, CASE WHEN LAG([Date]) OVER(PARTITION BY [name], [status] ORDER BY [date] ) = DATEADD(day, -1, [date]) THEN 1 ELSE 0 END grp
  FROM t

Рассчитать «что является промежуточной суммой текущего блока последовательных отчетов»:

SELECT 
  [name], 
  [status], 
  [date],
  1 + (SUM( grp ) OVER (PARTITION BY [name], [status] ORDER BY [date] ROWS BETWEEN 1 PRECEDING AND 0 FOLLOWING ) * grp) ct,
  row_number() over(partition by [name] order by [date] desc) rn
 FROM
 (
  SELECT *, CASE WHEN LAG([Date]) OVER(PARTITION BY [name], [status] ORDER BY [date] ) = DATEADD(day, -1, [date]) THEN 1 ELSE 0 END grp
  FROM t
 ) x

Тогда, конечно, все это, но без предложения where, чтобы вы могли видеть данные, которые мы отбрасываем:

SELECT [Name], [Status], ct as [Count]
FROM (
 SELECT 
  [name], 
  [status], 
  [date],
  1 + (SUM( grp ) OVER (PARTITION BY [name], [status] ORDER BY [date] ROWS BETWEEN 1 PRECEDING AND 0 FOLLOWING ) * grp) ct,
  row_number() over(partition by [name] order by [date] desc) rn
 FROM
 (
  SELECT *, CASE WHEN LAG([Date]) OVER(PARTITION BY [name], [status] ORDER BY [date] ) = DATEADD(day, -1, [date]) THEN 1 ELSE 0 END grp
  FROM t
 ) x
) y

Скрипка наконец проснулась:

http://www.sqlfiddle.com/#!18/77dae/2

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...