Наилучшая производительность при повторной выборке выборки из группируемого столбца

Этот вопрос касается функциональности first_value(), используя другую функцию или обходной путь.

Это также о "небольшом выигрыше в производительности" в больших таблицах. Использовать, например. max() в объясненном контексте ниже, требует ложных сравнений. Даже если быстро, это налагает дополнительные расходы.


Этот типичный запрос

SELECT x, y, count(*) as n 
FROM t 
GROUP BY x, y;

необходимо повторить все столбцы в GROUP BY, чтобы вернуть более одного столбца. Синтаксический сахар для этого состоит в использовании позиционных ссылок:

SELECT x, y, count(*) as n 
FROM t 
GROUP BY x, 2  -- imagine that 2, 3, etc. are repeated with x

Иногда для понимания сложного контекста иногда требуется не только сахар, но и некоторые семантики:

SELECT x, COALESCE(y,z), count(*) as n 
FROM t 
GROUP BY x, y, z  -- y and z are not "real need" grouping clauses?

Я могу представить много других сложных контекстов. Рассмотрим обычные решения:

SELECT x, max(y) as y, count(*) as n 
FROM t 
GROUP BY x  -- best semantic! no need for other columns here

где max() функция может быть любой "sample()" (например, первое или последнее значение). Производительность чего-то, что ничего не делает, лучше, чем max(), например. агрегированную функцию first_value(), но ей нужна WINDOW, поэтому потерянная производительность. Есть несколько старых предложений для реализации первых/последних функций agg в C.

Есть ли какая-либо функция "получить какое-то одно быстрое значение" с более высокой производительностью, чем max() или GROUP BY X,2,...?
Возможно, какая-то новая функция в недавнем выпуске?

Ответы

Ответ 1

Если вам действительно не нравится, какой элемент набора выбран, и если вам не нужно вычислять дополнительные агрегаты (например, count), есть быстрая и простая альтернатива с DISTINCT ON (x) без ORDER BY:

SELECT DISTINCT ON (x) x, y, z FROM t;

x, y и z относятся к одной и той же строке, но строка представляет собой произвольный выбор из каждого набора строк с тем же x.

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

SELECT DISTINCT ON (x) x, y, z, count(*) OVER (PARTITION BY x) AS x_count FROM t;

Рассмотрим последовательность событий в запросе SELECT:

В зависимости от требований могут быть более быстрые способы получения подсчетов:

В сочетании с GROUP BY единственным реалистичным вариантом, который я вижу для получения некоторой производительности, является расширение first_last_agg. Но не ожидайте многого.

Для других случаев использования без счета (включая простой случай в верхней части), есть более быстрые решения, в зависимости от вашего конкретного варианта использования. В частности, чтобы получить "первое" или "последнее" значение каждого набора. Эмулируйте развертку индекса. (Как @Mihai прокомментировал):

Ответ 2

Не является официальным источником, но некоторые мысли представляют собой довольно общий характер:

В общих агрегаторах можно обрабатывать все совпадающие строки. Из вашего текста вопроса вы можете настроить агрегаторы, которые пытаются идентифицировать определенные значения (макс, мин, первый, последний, n-й и т.д.). Этим могут воспользоваться структуры данных, которые поддерживают надлежащие значения для конкретного такого агрегатора. Тогда "выбор" этого значения может резко ускориться.
Например. некоторые базы данных отслеживают максимальные и минимальные значения столбцов.
Вы можете просмотреть эту поддержку как узкоспециализированные внутренние индексы, которые поддерживаются самой системой, а не под (прямым) контролем пользователя.

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

Назад к ускорению агрегаторов выборки.

С агрегаторами, которые должны обрабатывать все строки в общем случае, а не общую стратегию, которая позволяет короткое замыкание этого требования для агрегаторов, которые пытаются идентифицировать определенные значения (агрегаторы выборочных типов на данный момент), очевидно, что любая переформулировка запроса, который не приводит к сокращению набора строк, которые должны быть обработаны, завершатся аналогичное время.

Для ускорения таких запросов, кроме обработки всех строк, вам понадобится поддерживающая структура данных. В базах данных это обычно предоставляется в виде индекса.

Вы также можете воспользоваться специальными операциями выполнения, которые позволяют сократить количество строк для чтения.

С помощью pg вы можете обеспечить собственную реализацию индекса. Таким образом, вы можете добавить реализацию, которая наилучшим образом поддерживает специальный тип агрегатора, который вас интересует. (По крайней мере, для случаев, когда вам часто нужно часто запускать такие запросы.)

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

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

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