PostgreSQL выбирает ближайшие моменты времени, соответствующие заданным интервалам - PullRequest
0 голосов
/ 01 июля 2019

Я хочу выяснить, могу ли я решить следующую проблему в SQL или мне лучше выбрать значения на свой язык сценариев и просто выполнить массовое обновление оттуда.

Есть некоторые моменты времени, и есть некоторые временные интервалы, определяемые центром временного интервала и максимальной продолжительностью от центра, пусть это будет 10 минут для всех из них. Центры могут быть на любой продолжительности друг от друга, точки могут быть на любой продолжительности друг от друга. Нужно выбрать все временные интервалы вместе с одной или нулевыми точками, чтобы каждая точка либо не была назначена, либо назначена только одному интервалу. Если одна точка соответствует более чем одному интервалу или наоборот, точки должны выбираться так, чтобы общая продолжительность между точками и центрами интервалов была минимальной.

Пример данных

interval
id centertime
1 2001-01-01 12.00     # starts at 11.50 ends at 12.10
2 2001-01-01 12.15     # starts at 12.05 ends at 12.25
3 2001-01-01 12.20     # starts at 12.10 ends at 12.30

point
id time
21 2001-01-01 12.00     
22 2001-01-01 12.11
23 2001-01-01 12.17
24 2001-01-01 12.19

Желаемые результаты:

interval_id point_id
1 21
2 23
3 24

Объяснение

Точка 21 точно совпадает с центром интервала 1, и ничто иное, поэтому присваивается.

Точка 23 ближе к интервалу 2, чем к 3, но точка 24 еще ближе к 3, поэтому интервалу 3 назначается точка 24.

Точка 22 является ближайшей оставшейся точкой к интервалу 2, поэтому она назначена.

Точка 21 находится в интервале 2, но точка 22 доступна и ближе, поэтому 21 не назначен интервалу и не появляется в результатах.

точка 23 еще ближе к 3, поэтому 22 является ближайший оставшийся

1 Ответ

0 голосов
/ 02 июля 2019

Хорошо, я понял.

Он использует боковое соединение для вычисления длительности от каждого центрального времени до каждой точки и оборачивает его в дополнительный SELECT, чтобы получить только самое близкое совпадение, используя упорядоченную оконную функцию.

SELECT * FROM (
    SELECT
        i.id as interval_id,
        p.id as point_id,
        p.duration,
        ROW_NUMBER() OVER (PARTITION BY i.id ORDER BY duration) AS rownumber
    FROM "interval" i
    LEFT JOIN LATERAL (
        SELECT
            p1.id,
            p1."time",
            ABS(EXTRACT(EPOCH FROM i.centertime - p1."time")) as duration
        FROM "point" p1
        WHERE p1."time"
            BETWEEN i.centertime - interval '10 minute'
            AND i.centertime + interval '10 minute'
     ) p on true)
     AS q
WHERE rownumber=1;
...