Я изменил имя вашего столбца group
на grp
, потому что group
является зарезервированным словом в Postgres и каждом стандарте SQL и не должно использоваться в качестве идентификатора.
Я понимаю ваш вопрос следующим образом:
Получите два массива, отсортированные в одинаковом порядке сортировки, чтобы одна и та же позиция элемента соответствовала одной и той же строке в обоих массивах.
Используйте подзапрос или CTE и упорядочите строки перед агрегированием.
SELECT id, array_agg(grp) AS grp, array_agg(dt) AS dt
FROM (
SELECT *
FROM tbl
ORDER BY id, grp, dt
) x
GROUP BY id;
Это на быстрее , чем для использования индивидуального ORDER BY
предложения в агрегатной функции array_agg()
подобно @ Mosty демонстрирует (и существует с PostgreSQL 9.0).Мости также интерпретирует ваш вопрос по-разному и использует соответствующие инструменты для своей интерпретации.
Является ли ORDER BY
безопасным подзапросом?
Руководство:
Агрегатные функции array_agg
, json_agg
, [...], а также аналогичные пользовательские агрегатные функции выдают значимо разные значения результата в зависимости от порядка входных значений.Этот порядок не указан по умолчанию, но им можно управлять, написав предложение ORDER BY
в совокупном вызове, как показано в разделе 4.2.7 .Альтернативно, подача входных значений из отсортированного подзапроса обычно работает.Например:
SELECT xmlagg(x) FROM (SELECT x FROM test ORDER BY y DESC) AS tab;
Помните, что этот подход может завершиться ошибкой, если внешний уровень запроса содержит дополнительную обработку, такую как объединение, поскольку это может привести к переупорядочению вывода подзапроса до вычисления агрегата.
Так что да, в этом примере это безопасно.
Без подзапроса
Если вам действительно нужно решение без подзапроса Вы можете:
SELECT id
, array_agg(grp ORDER BY grp)
, array_agg(dt ORDER BY grp, dt)
FROM tbl
GROUP BY id;
Обратите внимание на ORDER BY grp, dt
.Я сортирую по dt
в дополнение к разрыву связей и делаю порядок сортировки однозначным.Однако для grp
не требуется.
Существует также совершенно другой способ сделать это с оконными функциями :
SELECT DISTINCT ON (id)
id
, array_agg(grp) OVER w AS grp
, array_agg(dt) OVER w AS dt
FROM tbl
WINDOW w AS (PARTITION BY id ORDER BY grp, dt
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
ORDER BY id;
Обратите внимание на DISTINCT ON (id)
вместо DISTINCT
, который дает тот же результат, но работает на порядок быстрее, потому что нам не нужна дополнительная сортировка.
Я провел несколько тестов, и это почти так же быстро, как и два других решения.Как и ожидалось, версия подзапроса была еще самой быстрой.Проверьте с EXPLAIN ANALYZE
, чтобы убедиться в этом.