Суммирование по подмножествам из одного ряда в другой по условию - PullRequest
0 голосов
/ 10 мая 2018

Я не очень хорошо разбираюсь в синтаксисе SQL и пытаюсь понять, как агрегировать простой набор данных.


Проблема:

Исходя из отметок времени 'Open' и 'Closed', я должен суммировать количество времени, которое пользователи проводят на каждой рабочей станции, используя приведенную ниже таблицу истории входа.


Требования:

  • Строки с пустыми столбцами рабочих станций или имен пользователей должны быть помечены как недействительные
  • Status 'для каждой строки, упорядоченной по отметке времени, которая не начинается с 'Open' или заканчивается 'Closed' также должен быть помечен как недействительный
  • Множественный 'Open' status ', прежде чем 'Closed' все еще может считаться действительным, но сумма должна начинаться с первого вхождения 'Open'

+-----------------+-------------+--------+------------+-------------------------+
|       KEY       | WORKSTATION | STATUS |  USERNAME  |        TIMESTAMP        |
+-----------------+-------------+--------+------------+-------------------------+
| 181861-0001-001 |             | Closed |            | 2015-07-01 18:19:48.527 |
| 181861-0001-001 |             | Closed |            | 2015-07-01 20:20:46.383 |
| 181861-0001-001 | 1AHVW       | Open   | ANDJOH0427 | 2015-07-01 13:18:46.547 |
| 181861-0001-001 | 1AHVW       | Closed | ANDJOH0427 | 2015-07-01 14:11:41.920 |
| 181861-0001-001 | 1ALVW       | Closed | DWYGRE0609 | 2015-07-01 18:29:39.127 |
| 181861-0001-001 | 1ALVW       | Closed | DWYGRE0609 | 2015-07-01 18:29:40.300 |
| 181861-0001-001 | 1AHVW       | Closed | HORDOU0521 | 2015-07-01 19:27:34.667 |
| 181861-0001-001 | 1AHVW       | Closed | HORDOU0521 | 2015-07-01 19:44:36.167 |
| 181861-0001-001 | 1AQCI       | Open   | POUJON702  | 2015-07-02 00:46:37.540 |
| 181861-0001-001 | 1ALVW       | Open   | PRIADA747  | 2015-07-01 14:51:02.937 |
| 181861-0001-001 | 1ALVW       | Open   | PRIADA747  | 2015-07-01 15:29:48.357 |
| 181861-0001-001 | 1ALVW       | Open   | PRIADA747  | 2015-07-01 16:13:20.953 |
| 181861-0001-001 | 1ALVW       | Open   | PRIADA747  | 2015-07-01 17:49:42.717 |
| 181861-0001-001 | 1ALVW       | Closed | PRIADA747  | 2015-07-01 17:53:28.217 |
| 181861-0001-001 | 1ALVW       | Open   | PRIADA747  | 2015-07-01 18:34:11.043 |
| 181861-0001-001 | 1ALVW       | Closed | PRIADA747  | 2015-07-01 19:20:11.540 |
+-----------------+-------------+--------+------------+-------------------------+

Мое (почти) решение:

SELECT Project, username, Workstation, 
min(case when [Status] = 'Open' then [TimeStamp] end) AS [Started],
max(case when [Status] = 'Closed' then [TimeStamp] end) as [Ended],
DATEDIFF(second, min(case when [Status] = 'Open' then [TimeStamp] end), max(case when [Status] = 'Closed' then [TimeStamp] end)) AS ActualSeconds
FROM History
GROUP BY Project, username, Workstation

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

Таким образом, мне нужно найти MINкаждого набора между состояниями 'Open' и 'Closed'.

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

+-----------------+-------------+------------+-------------------------+-------------------------+---------+
|       KEY       | WORKSTATION |  USERNAME  |        START TIME       |        END TIME         | SECONDS |
+-----------------+-------------+------------+-------------------------+-------------------------+---------+
| 181861-0001-001 |             |            | NULL                    | 2015-07-01 18:19:48.527 | NULL    |
| 181861-0001-001 | 1AHVW       | ANDJOH0427 | 2015-07-01 13:18:46.547 | 2015-07-01 14:11:41.920 | 3175    |
| 181861-0001-001 | 1ALVW       | DWYGRE0609 | NULL                    | 2015-07-01 18:29:39.127 | NULL    |
| 181861-0001-001 | 1AHVW       | HORDOU0521 | NULL                    | 2015-07-01 19:27:34.667 | NULL    |
| 181861-0001-001 | 1AQCI       | POUJON702  | 2015-07-02 00:46:37.540 | NULL                    | NULL    |
| 181861-0001-001 | 1ALVW       | PRIADA747  | 2015-07-01 14:51:02.937 | 2015-07-01 17:53:28.217 | 10945   |
| 181861-0001-001 | 1ALVW       | PRIADA747  | 2015-07-01 18:34:11.043 | 2015-07-01 19:20:11.540 | 2760    |
+-----------------+-------------+------------+-------------------------+-------------------------+---------+

Ответы [ 2 ]

0 голосов
/ 10 мая 2018

Это должно быть близко:

with data as (
    select *,
        row_number() over (partition by workstation order by timestamp) as tn,
        row_number() over (partition by workstation order by username, timestamp) as un,
        sum(case when status = 'Closed' then 1 end) over (
            partition by workstation, username order by timestamp desc) as sn
    from t
)
select workstation, username,
    min(case when status = 'Open' then timestamp end) as start_time,
    max(case when status = 'Closed' then timestamp end) as end_time,
    datediff(second,
        min(case when status = 'Open' then timestamp end),
        max(case when status = 'Closed' then timestamp end)) as diff,
    datediff(millisecond,
        min(case when status = 'Open' then timestamp end), 
        max(case when status = 'Closed' then timestamp end)) / 1000 as diff2,
case when count(*) > 1 then 'Valid' else 'Invalid' end as flag
from data
group by workstation, username, tn - un, sn;

http://rextester.com/JXS10553

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

0 голосов
/ 10 мая 2018

Это на самом деле работа с догадками, так как данные примера и ожидаемый результат, предоставленные OP, похоже, не коррелируют. Это дает правильные результаты для USERNAME NULL, 'ANDJOH0427', 'HORDOU0521' и 'DWYGRE0609', однако возвращает результат для 'PRIADA747' (который исключен из ожидаемого набора результатов) и дает совсем другой ответ для 'POUJON702':

USE Sandbox;
GO

CREATE TABLE #Sample ([KEY] varchar(15),
                      WORKSTATION varchar(5),
                      [STATUS] varchar(6),
                      USERNAME varchar(10),
                      [TIMESTAMP] datetime);

INSERT INTO #Sample
VALUES ('181861-0001-001',NULL,'Closed',NULL,'2015-07-01T18:19:48.527'),
       ('181861-0001-001',NULL,'Closed',NULL,'2015-07-01T20:20:46.383'),
       ('181861-0001-001','1AHVW','Open','ANDJOH0427','2015-07-01T13:18:46.547'),
       ('181861-0001-001','1AHVW','Closed','ANDJOH0427','2015-07-01T14:11:41.920'),
       ('181861-0001-001','1ALVW','Closed','DWYGRE0609','2015-07-01T18:29:39.127'),
       ('181861-0001-001','1ALVW','Closed','DWYGRE0609','2015-07-01T18:29:40.300'),
       ('181861-0001-001','1AHVW','Closed','HORDOU0521','2015-07-01T19:27:34.667'),
       ('181861-0001-001','1AHVW','Closed','HORDOU0521','2015-07-01T19:44:36.167'),
       ('181861-0001-001','1AQCI','Open','POUJON702','2015-07-02T00:46:37.540'),
       ('181861-0001-001','1ALVW','Open','PRIADA747','2015-07-01T14:51:02.937'),
       ('181861-0001-001','1ALVW','Open','PRIADA747','2015-07-01T15:29:48.357'),
       ('181861-0001-001','1ALVW','Open','PRIADA747','2015-07-01T16:13:20.953'),
       ('181861-0001-001','1ALVW','Open','PRIADA747','2015-07-01T17:49:42.717'),
       ('181861-0001-001','1ALVW','Closed','PRIADA747','2015-07-01T17:53:28.217'),
       ('181861-0001-001','1ALVW','Open','PRIADA747','2015-07-01T18:34:11.043'),
       ('181861-0001-001','1ALVW','Closed','PRIADA747','2015-07-01T19:20:11.540');
GO
SELECT *
FROM #Sample;
GO

WITH Starts AS(
    SELECT [KEY],
           WORKSTATION,
           USERNAME,
           [TIMESTAMP],
           NULLIF(MIN(ISNULL(CASE STATUS WHEN 'Open' THEN [TIMESTAMP] END,'20550101')) OVER (PARTITION BY [KEY], WORKSTATION, USERNAME),'20550101') AS StartTime
    FROM #Sample S)
SELECT [KEY],
       WORKSTATION,
       USERNAME,
       StartTime,
       MAX([TIMESTAMP]) AS EndTime,
       DATEDIFF(SECOND, StartTime, MAX([TIMESTAMP])) AS Seconds
FROM Starts
GROUP BY [KEY],
         WORKSTATION,
         USERNAME,
         StartTime;
GO      
DROP TABLE #Sample;
...