ОК, не думал, что у меня будет время написать это, отсюда и комментарий, но:
Давайте возьмем данные и поместим столбец с 0 или 1 в зависимости от того, было ли действие предыдущей строки одинаковым. или другой, для каждого компьютера:
SELECT
t.*,
CASE WHEN action = LAG(action) OVER(PARTITION BY computer ORDER BY time) THEN 0 ELSE 1 END as differs
FROM
t
Теперь давайте превратим поток 0 и 1 в инкрементный счетчик:
WITH cte AS(
SELECT
t.*,
CASE WHEN action = LAG(action) OVER(PARTITION BY comp ORDER BY time) THEN 0 ELSE 1 END as differs
FROM
t
)
SELECT cte.*, SUM(differs) OVER(PARTITION BY comp ORDER BY time ROWS UNBOUNDED PRECEDING) as run_tot_dif
Теперь давайте сгруппируемся по этому счетчику для каждого компьютера и возьмем мин. / макс раз. Я отмечаю, что ваше время - это не конец текущего действия, а начало следующего действия. Для этого мы добавим функцию, которая выбирает следующий раз для каждого компьютера для каждой строки в первом cte, чтобы мы могли выбрать его позже, и мы также COALESCE это так, чтобы нули, которые это вызывает в конце периода времени стать:
WITH cte_diff AS(
SELECT
t.*,
COALESCE(LEAD(time) OVER(PARTITION BY comp ORDER BY time), :endtime) next_start_time,
CASE WHEN action = LAG(action) OVER(PARTITION BY comp ORDER BY time) THEN 0 ELSE 1 END as differs
FROM
t
WHERE time BETWEEN :starttime and :endtime --parameterize it
),
cte_runtot AS(
SELECT
cte_diff.*,
SUM(differs) OVER(PARTITION BY comp ORDER BY time ROWS UNBOUNDED PRECEDING) as run_tot_dif
FROM
cte_diff
)
SELECT
comp,
action,
MIN(time) as start_time,
MAX(next_start_time) as end_time
FROM
cte_runtot
GROUP BY
comp, action, run_tot_dif
ORDER BY
start_time
Я хотел бы отметить, что я думаю, что вы допустили ошибку в желаемом выводе - действие comp2 B выполнялось до тех пор, пока comp2 не начало действие C при t = 9, а не до тех пор, пока comp1 не начало действие A при t = 7 .. Если я не понял ваши требования
пс; Вы можете несколько свернуть его, не используя CTE, но это может не означать, что он более читабелен:
SELECT
comp,
action,
MIN(time) as start_time,
MAX(next_start) as end_time
FROM
(
SELECT
x.*,
SUM(diff) OVER(PARTITION BY comp ORDER BY time ROWS UNBOUNDED PRECEDING) as run_tot_diff
FROM
(
SELECT
t.*,
COALESCE(LEAD(time) OVER(PARTITION BY comp ORDER BY time), 9999) next_start,
CASE WHEN action = LAG(action) OVER(PARTITION BY comp ORDER BY time) THEN 0 ELSE 1 END diff
FROM t
WHERE time BETWEEN 0 and 11
) x
)y
GROUP BY
comp, action, run_tot_diff
ORDER BY
start_time
Это не может быть значительно уменьшено, потому что Oracle не разрешает оконные функции в GROUP BY, внутри других оконных функций или внутри агрегатов (SQL Сервер с этим справляется меньше)