Получить количество значений в разных подгруппах

#postgresql #count #where-clause #window-functions

Вопрос:

Мне нужно удалить некоторые строки в наборе данных, из которых значение speed равно нулю и длится более N раз (предположим, что N равно 2). Структура таблицы demo выглядит следующим образом:

ID Автомобиль Скорость время
1 foo 0 1
2 foo 0 2
3 foo 0 3
4 foo 1 4
5 foo 1 5
6 foo 0 6
7 бар 0 1
8 бар 0 2
9 бар 5 3
10 бар 5 4
11 бар 5 5
12 бар 5 6

Затем я надеюсь создать таблицу, подобную приведенной ниже, используя window_function :

ID Автомобиль Скорость время длительное
1 foo 0 1 3
2 foo 0 2 3
3 foo 0 3 3
4 foo 1 4 2
5 foo 1 5 2
6 foo 0 6 1
7 бар 0 1 2
8 бар 0 2 2
9 бар 5 3 4
10 бар 5 4 4
11 бар 5 5 4
12 бар 5 6 4

Тогда я могу легко исключить эти строки, используя WHERE NOT (speed = 0 AND lasting > 2)

Поместите код, который я пробовал здесь, но он не вернул ожидаемое значение, и я предполагаю, что это FROM (SELECT ... FROM (SELECT ... может быть не лучшая практика для решения проблемы:

 SELECT g3.*, count(id) OVER (PARTITION BY car, cumsum ORDER BY id) as num   
  FROM (SELECT g2.*, sum(grp2) OVER (PARTITION BY car ORDER BY id) AS cumsum             
    FROM (SELECT g1.*, (CASE ne0 WHEN 0 THEN 0 ELSE 1 END) AS grp2                            
      FROM (SELECT g.*, speed - lag(speed, 1, 0) OVER (PARTITION BY car) AS ne0              
        FROM (SELECT *, row_number() OVER (PARTITION BY car) AS grp FROM demo) g ) g1 ) g2 ) g3                                                                                       
ORDER BY id;
 

Ответ №1:

Вы можете использовать функцию window LAG() для проверки предыдущего speed значения для каждой строки и SUM() функцию window для создания групп для непрерывных значений.
Затем с COUNT() помощью функции window вы можете подсчитать количество строк в каждой группе, чтобы отфильтровать строки с 0 speed в группах, которые имеют более 2 строк:

 SELECT id, car, speed, time
FROM (
  SELECT *, COUNT(*) OVER (PARTITION BY car, grp) counter
  FROM (
    SELECT *, SUM(flag::int) OVER (PARTITION BY car ORDER BY time) grp
    FROM (
      SELECT *, speed <> LAG(speed, 1, speed - 1) OVER (PARTITION BY car ORDER BY time) flag
      FROM demo
    ) t  
  ) t
) t
WHERE speed <> 0 OR counter <= 2
ORDER BY id;
 

Смотрите демонстрацию.

Комментарии:

1. Спасибо. Я замечаю еще один момент в решении, который: Могу ли я узнать время для использования ORDER BY во OVER фразе, пожалуйста. Иногда я находил запрос, необходимый ORDER BY для получения правильной обратной связи, но иногда нет.

2. @FanLiu имеет смысл использовать столбец time в предложении ORDER BY всех разделов. Я использовал id , потому что это то, что я вижу в вашем коде.

3. полностью согласен с идеей использования time поля. мой вопрос решен отлично. большое спасибо 🙂