#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
поля. мой вопрос решен отлично. большое спасибо 🙂