2015-12-16 4 views
0

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

Если я определяю и заполнить свою таблицу следующим образом:

CREATE TABLE test_fill_null (
    date INTEGER, 
    value INTEGER 
); 

INSERT INTO test_fill_null VALUES 
    (1,2), 
    (2, NULL), 
    (3, 45), 
    (4,NULL), 
    (5, null); 

SELECT * FROM test_fill_null ; 
date | value 
------+------- 
    1 |  2 
    2 |  
    3 | 45 
    4 |  
    5 |  

Тогда я просто должны заполнить так:

UPDATE test_fill_null t1 
SET value = (
    SELECT t2.value 
    FROM test_fill_null t2 
    WHERE t2.date <= t1.date AND value IS NOT NULL 
    ORDER BY t2.date DESC 
    LIMIT 1 
); 

SELECT * FROM test_fill_null; 
date | value 
------+------- 
    1 |  2 
    2 |  2 
    3 | 45 
    4 | 45 
    5 | 45 

Но сейчас, я в запросе, как этот:

WITH 
    pre_table AS(
     SELECT 
      id1, 
      id2, 
      tms, 
      CASE 
       WHEN tms - lag(tms) over w < interval '5 minutes' THEN NULL 
       ELSE id2 
      END as group_id 
     FROM 
      table0 
     window w as (partition by id1 order by tms) 
    ) 

Где установить group_id до id2, когда предыдущая точка удалена от более 5 минут, в противном случае - null. Поступая таким образом, я хочу получить группу очков, которые следуют друг за другом менее чем за 5 минут, а промежутки между группами - более 5 минут.

Тогда я не знаю, как действовать. Я пробовал:

SELECT distinct on (id1, id2) 
     t0.id1, 
     t0.id2, 
     t0.tms, 
     t1.group_id 
    FROM 
     pre_table t0 
     LEFT JOIN (
      select 
       id1, 
       tms, 
       group_id 
      from pre_table t2 
      where t2.group_id is not null 
      order by tms desc 
     ) t1 
     ON 
      t1.tms <= t0.tms AND 
      t1.id1 = t0.id1 
    WHERE 
     t0.id1 IS NOT NULL 
    ORDER BY 
     id1, 
     id2, 
     t1.tms DESC 

Но в конечном итоге у меня есть некоторые группы с двумя последовательными точками, которые находятся на расстоянии от более чем за 5 минут. В этом случае их должно быть две разные группы.

+1

Так с одним значением NULL через каждые 5 минут, та же группа может быть увековечены на неопределенное время, не так ли? Как всегда: ваша версия Postgres, пожалуйста. И хороший тестовый пример с самого начала оказывается * не * применимым к вашей реальной проблеме. Было бы более разумно предоставить тестовый пример для вашей реальной проблемы. –

+0

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

+0

Предполагаемый основной порядок строк также не определен. Вы заказываете 'id1',' tms' или 'id1, id2, tms'? –

ответ

1

«выберите в отборное» чаще называют «подвыборкой» или «подзапросов» В вашем конкретном случае это коррелируется подзапрос. LATERAL присоединяется (новый в Postgres 9.3) может в значительной степени заменить коррелированные подзапросы с более гибкими решениями:

Я не думаю, что вам нужно либо здесь.

Для вашего первого случая этого запрос, вероятно, быстрее и проще, хотя:

SELECT date, max(value) OVER (PARTITION BY grp) AS value 
FROM (
    SELECT *, count(value) OVER (ORDER BY date) AS grp 
    FROM test_fill_null 
    ) sub; 

count() подсчитывает только ненулевые значения, так grp увеличивается с каждым ненулевым value, тем самым образуя группы, желательно. Тривиально выбрать один нуль value за grp внешний SELECT.


Для вашего второго случая, я предполагаю, что начальный порядок строк определяется (id1, id2, tms), как указано одним из ваших запросов.

SELECT id1, id2, tms 
    , count(step) OVER (ORDER BY id1, id2, tms) AS group_id 
FROM (
    SELECT *, CASE WHEN lag(tms, 1, '-infinity') OVER (PARTITION BY id1 ORDER BY id2, tms) 
         < tms - interval '5 min' 
        THEN true END AS step 
    FROM table0 
    ) sub 
ORDER BY id1, id2, tms; 

Адаптировать к вашему фактическому заказу.Один из них может покрыть:

PARTITION BY id1 ORDER BY id2 -- ignore tms 
PARTITION BY id1 ORDER BY tms -- ignore id2 

SQL Fiddle с расширенным примером.

Похожие:

+0

Я попробовал SQL Fiddle, и мне пришлось исправлять данные, чтобы отразить мои. Сделав это, я увидел, что id1 не был хорошим именем, и я вводил вас в заблуждение. id1 можно рассматривать как внешний ключ. Я исправил следующее: ВСТАВИТЬ В таблицу 0 ЗНАЧЕНИЯ (1,2, '2015-12-16 16: 09: 00 + 01'), (1,3, '2015-12-16 16: 11: 00+ 01 '), (2,4,' 2015-12-16 16: 10: 00 + 01 '), (2,5,' 2015-12-16 16: 12: 00 + 01 '), (1,6, «2015-12-16 16: 13: 01 + 01»); Поскольку исправление вашего решения, похоже, не работает, но я буду работать над этим. Если я полностью пойму ваш ответ, я смогу его исправить. – Borbag

+0

Фактически для моего второго случая вы дали мне другой способ получить мой «запрос контекста», а не «запрос маркировки». (не так уж и отличается, вы просто передаете «tms» на другую сторону неравенства). С помощью вашего ответа в первом случае я закончил его так, чтобы я этого захотел. [sql Fiddle) (http://sqlfiddle.com/#!15/af4e64/9) Что вы думаете об этом? – Borbag

+0

Я вэнь от 6 минут с моим запросом до 5 секунд с вашим, спасибо! Просто отредактируйте свой ответ со второй частью запроса. В моей скрипке я подтвержу ваш ответ. – Borbag

0

При редактировании моего вопроса я нашел решение. Это довольно низко, хотя, намного ниже, чем мой пример в таблице. Любое предложение по его улучшению?

SELECT 
     t2.id1, 
     t2.id2, 
     t2.tms, 
     (
      SELECT t1.group_id 
      FROM pre_table t1 
      WHERE 
       t1.tms <= t2.tms 
       AND t1.group_id IS NOT NULL 
       AND t2.id1 = t2.id1 
      ORDER BY t1.tms DESC 
      LIMIT 1 
     ) as group_id 
    FROM 
     pre_table t2 
    ORDER BY 
     t2.id1 
     t2.id2 
     t2.tms 

Так как я уже сказал, избранная в пределах выбранного