Расчет длины пробега в postgresql - PullRequest
0 голосов
/ 07 июня 2018

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

CREATE TABLE runs (time int, ok int);

INSERT INTO runs VALUES
(1, NULL),
(2, 1),
(3, 1),
(4, 1),
(5, NULL),
(6, NULL),
(7, 1),
(8, 1),
(9, NULL),
(10, 1)

Я хотел бы использовать оконную функцию (я думаю), чтобы определить длину этих прогонов "ok" -ness.Таким образом, конечный набор данных должен выглядеть следующим образом:

time | ok_length
----------------
 2   |   3
 7   |   2
 10  |   1

Насколько я знаю:

SELECT
  time,
  ok,
  CASE WHEN
    LAG(ok) OVER (ORDER BY time) IS NOT null
    THEN SUM(ok) OVER (ORDER BY time) END
FROM runs
ORDER BY time

Но это совершенно неправильно.Кто-нибудь может помочь?Возможно, мне нужно что-то сделать с фреймом в конце оконной функции, но у этого фрейма должна быть условная остановка, когда он достигает значения NULL.Вот сценарий SQL, с которым я работаю: http://sqlfiddle.com/#!17/98bf4/3

Ответы [ 2 ]

0 голосов
/ 08 июня 2018

Вот несколько менее подробное решение:

select distinct
        min(time) over (partition by gp)
        , sum(ok) over (partition by gp)
from (
        select *
                , time - row_number() over (partition by ok order by time asc) gp
        from runs
        where ok is not null
) rs
order by 1
0 голосов
/ 07 июня 2018

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

  • group_start_cte - задержка для обозначения строк, являющихся началом другой логической группировки.
  • group_cte - накопленная сумма для присвоения всем строкам идентификатора группы.
  • group_cnt - считать эти разбиения по идентификатору логической группировки.
  • first_time_for_group -получите время в начале группы.

А затем в конце мы объединяем group_cnt и first_time_for_group вместе:

WITH
group_start_cte AS (
SELECT
    TIME,
    ok,
    CASE
      WHEN LAG(ok) OVER (ORDER BY TIME asc) is distinct from ok
      THEN TRUE
    END AS group_start
FROM
    runs
),
group_cte AS (
SELECT
    TIME,
    ok,
    group_start,
    SUM(CASE WHEN group_start THEN 1 ELSE 0 END) OVER (ORDER BY TIME asc) AS grp_id
FROM
    group_start_cte
),
first_time_for_group as (
SELECT
    time,
    grp_id
FROM
    group_cte
WHERE
    group_start IS TRUE
),
group_cnt AS (
SELECT
    grp_id,
    count(*) AS ok_length
FROM
    group_cte
WHERE
    ok IS NOT NULL
GROUP BY
    grp_id
)
SELECT
    TIME,
    ok_length
FROM
    group_cnt
    LEFT JOIN first_time_for_group
    USING (grp_id)
ORDER BY
    time ASC
;
...