У меня есть большой набор данных (~ 2 млн строк), который описывает данные об автомобилях, когда они перемещаются по большому парковочному комплексу. То есть каждое транспортное средство сканируется, когда оно проходит через несколько «зон» в конструкции. Это выглядит примерно так:

+--------+----------------+----------+---------------------+
|   id   | zone_camera_id |  plate   |      timestamp      |
+--------+----------------+----------+---------------------+
| 453445 | Z05-C01        | AAAABBBB | 2020-06-25 08:02:23 |
| 453446 | Z05-C02        | AAAABBBB | 2020-06-25 08:04:55 |
| 453447 | Z03-C01        | CCCCDDDD | 2020-06-25 08:05:19 |
| 453448 | Z02-C02        | AAAABBBB | 2020-06-25 08:05:23 |
| 453449 | Z07-C03        | CCCCDDDD | 2020-06-25 08:09:08 |
| 453450 | Z07-C04        | CCCCDDDD | 2020-06-25 08:10:01 |
| 453451 | Z04-C04        | AAAABBBB | 2020-06-25 08:11:44 |
| 453452 | Z04-C01        | AAAABBBB | 2020-06-25 08:11:59 |
| 453453 | Z04-C03        | AAAABBBB | 2020-06-25 08:12:06 |
| 453454 | Z05-C03        | AAAABBBB | 2020-06-25 08:13:00 |
+--------+----------------+----------+---------------------+

camera_id распадается следующим образом: <Zone ID>-<Camera ID>, а <Camera ID> обычно не имеет значения; автомобиль, обнаруженный Z05-C01, эквивалентен тому же автомобилю, обнаруженному Z05-C04.

Я могу быстро GROUP BY zone_camera_id использовать LEFT(), например:

SELECT Count(*) AS scan_count,
       LEFT(zone_camera_id, 3) AS zone
FROM   vehicle_scans
WHERE  plate = 'AAAABBBB'
GROUP  BY LEFT(zone_camera_id, 3)  

И я вижу:

+------------+------+
| scan_count | zone |
+------------+------+
| Z05        |    3 |
| Z02        |    1 |
| Z04        |    3 |
+------------+------+

Это отличная информация, однако она не дает никакого представления о «пути», который выбрал водитель. Этот запрос не имеет представления о временной последовательности, так что если драйвер запускается в Z02, переходит в Z05 и * возвращается в * Z02, эти Z02 сканирования будут смешиваться вместе.

Что я хочу сделать, так это удалить "повторяющиеся" сканирования, когда водитель сканировался несколько раз в одной и той же зоне подряд (не покидал зону, как в ID=453445,453446, но НЕ ID=453454 когда водитель вернулся), но так и не покинул зону. По сути, я хочу знать, когда водитель вошел в зону и покинул зону, без посещения другой зоны в течение этого периода времени .

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

+---------+------------+---------------------+---------------------+
| zone_id | scan_count |     enter_time      |      exit_time      |
+---------+------------+---------------------+---------------------+
| Z05     |          2 | 2020-06-25 08:02:23 | 2020-06-25 08:04:55 |
| Z02     |          1 | 2020-06-25 08:05:23 | 2020-06-25 08:05:23 |
| Z04     |          3 | 2020-06-25 08:11:44 | 2020-06-25 08:12:06 |
| Z05     |          1 | 2020-06-25 08:13:00 | 2020-06-25 08:13:00 |
+---------+------------+---------------------+---------------------+

Z05 появляется дважды, потому что они посетили зону дважды, с двумя другими посещениями зоны между ними.

Вот что я пробовал, используя Min() и Max():

SELECT Count(*)                 AS scan_count,
       LEFT(camera_zone_id, 3)  AS zone_id,
       Min(timestamp)           AS enter_time,
       Max(timestamp)           AS exit_time
FROM   vehicle_scans
WHERE  plate = 'AAAABBBB'
GROUP  BY LEFT(camera_zone_id, 3)
ORDER  BY enter_time

Это отличная информация, и она соответствует структуре моего желаемого результата, однако значения Min() и Max() отражают абсолютные минимальные и максимальные временные метки для сканирований в этой зоне, в отличие от минимальных и максимальных временных меток. для последовательностей сканирования в особых зонах. В приведенных выше случаях, например с AAAABBBB, транспортное средство начинается в Z05, посещает еще две зоны и затем возвращается в Z05. В приведенном выше запросе используется первое сканирование Z05 и самое последнее сканирование Z05, хотя между ними были еще две посещенные зоны (Z02 и Z04). Я ищу каждое непрерывное "посещение" зоны в отдельной строке, удаляя "повторяющиеся" сканированные изображения, в то время как они продолжают сканироваться в LEFT(camera_zone_id, 3).

Есть ли метод на основе SQL для группировки этих строк в непрерывную последовательность?

Спасибо!

1
David 25 Июн 2020 в 16:09

1 ответ

Лучший ответ

Это может быть проблема с зазорами и островками, но вам нужно закрепить это пластиной.

Разница номеров строк удобна:

select plate, left(camera_zone_id, 3), min(timestamp), max(timestamp)
from (select vs.*,
              row_number() over (partition by plate, left(camera_zone_id, 3) order by timestamp) as seqnum_pc,
              row_number() over (partition by plate order by timestamp) as seqnum_p
      from vehicle_scans vs
     ) vs
group by plate, (seqnum_pc - seqnum_p)
1
Gordon Linoff 25 Июн 2020 в 13:19