У меня довольно сложный запрос SQL, который сначала извлекает некоторые данные в CTE, а затем выполняет несколько самосоединений в CTE для вычисления значения. Вот сокращенный пример с некоторыми упрощениями нашего приложения:
WITH subset AS (
SELECT time, value, device_id FROM raw_data
WHERE device_id IN (1, 2, 3)
AND time BETWEEN '2019-01-01 00:00:00'::timestamp AND '2019-01-15 00:00:00'::timestamp
)
SELECT
time,
(("device_1".value + "device_2".value) / "device_3".value) as value
FROM
(
SELECT * FROM subset
WHERE device_id = 1
) "device_1"
INNER JOIN
(
SELECT * FROM subset
WHERE device_id = 2
) "device_2"
ON "device_1".time = "device_2".time
INNER JOIN
(
SELECT * FROM subset
WHERE device_id = 3
) "device_3"
ON "device_3".time = "device_2".time
Запрос генерируется автоматически и может масштабироваться до сложных вычислений по значениям потенциально десятков устройств. По соображениям производительности мы хотели бы разбить на страницы результаты этого запроса, поскольку используемый временной диапазон может быть большим. Ключевым ограничением является то, что данные могут иметь промежутки во времени, но мы хотим возвращать постоянное количество строк на странице.
Мы рассмотрели использование LIMIT per_page OFFSET start
в конце запроса, который будет стандартным подход, но это не дает нам никакой скорости и запрос выполняет то же самое. Это имеет смысл, потому что в этом случае LIMIT / OFFSET выполняется после того, как все данные были извлечены, объединены и вычислены, и он просто возвращает фрагмент данных, который уже вычислен. Это существенно не уменьшает скорость выполнения запроса.
Мы рассмотрели разбиение на страницы данных, извлеченных в CTE, то есть вычисление того, какой временной диапазон соответствует интересующей странице, и затем использование этого временного диапазона в BETWEEN. пункт CTE. Это сработало бы, но проблема в том, что мы не можем надежно вычислить этот временной интервал, поскольку некоторые переменные могут содержать пробелы. Поэтому, если мы вычисляем 100 строк, чтобы они были окном 2 дня, и выбираем 2 дня, есть вероятность, что мы получим менее 100 строк, если device_2 не записал данные в какой-то момент в этом окне. Для вычисления эти точки данных будут отброшены во ВНУТРЕННИХ СОЕДИНЕНИЯХ.
Вопрос в том, существует ли эффективный способ разбить на страницы этот запрос или реструктурировать его, чтобы обеспечить быструю разбивку на страницы, учитывая эти ограничения? Например, есть ли способ указать планировщику запросов «присоединиться, пока вы не сопоставите 100 результатов, соответствующих условиям объединения, и остановитесь на этом». Мы запускаем это на PostgreSQL, если это имеет значение.