У меня есть таблица, содержащая два столбца перестановок / комбинаций целочисленных массивов, и третий столбец, содержащий значение, например, так:
CREATE TABLE foo
(
perm integer[] NOT NULL,
combo integer[] NOT NULL,
value numeric NOT NULL DEFAULT 0
);
INSERT INTO foo
VALUES
( '{3,1,2}', '{1,2,3}', '1.1400' ),
( '{3,1,2}', '{1,2,3}', '0' ),
( '{3,1,2}', '{1,2,3}', '1.2680' ),
( '{3,1,2}', '{1,2,3}', '0' ),
( '{3,1,2}', '{1,2,3}', '1.2680' ),
( '{3,1,2}', '{1,2,3}', '0' ),
( '{3,1,2}', '{1,2,3}', '0' ),
( '{3,1,2}', '{1,2,3}', '1.2680' ),
( '{3,1,2}', '{1,2,3}', '0.9280' ),
( '{3,1,2}', '{1,2,3}', '0' ),
( '{3,1,2}', '{1,2,3}', '1.2680' ),
( '{3,1,2}', '{1,2,3}', '0' ),
( '{3,1,2}', '{1,2,3}', '0' ),
( '{3,1,2}', '{1,2,3}', '1.2680' ),
( '{3,1,2}', '{1,2,3}', '0' ),
( '{3,2,1}', '{1,2,3}', '0' ),
( '{3,2,1}', '{1,2,3}', '0.8000' )
Я хочу узнать среднее и стандартное отклонение для каждой перестановки, а также для каждой комбинации. Я могу сделать это с помощью этого запроса:
SELECT
f1.perm,
f2.combo,
f1.perm_average_value,
f2.combo_average_value,
f1.perm_stddev,
f2.combo_stddev,
f1.perm_count,
f2.combo_count
FROM
(
SELECT
perm,
combo,
avg( value ) AS perm_average_value,
stddev_pop( value ) AS perm_stddev,
count( * ) AS perm_count
FROM foo
GROUP BY perm, combo
) AS f1
JOIN
(
SELECT
combo,
avg( value ) AS combo_average_value,
stddev_pop( value ) AS combo_stddev,
count( * ) AS combo_count
FROM foo
GROUP BY combo
) AS f2 ON ( f1.combo = f2.combo );
Однако этот запрос может быть довольно медленным, когда у меня много данных, потому что таблицу "foo" (которая на самом деле состоит из 14 разделов, каждый из которых содержит примерно 4 миллиона строк) необходимо сканировать дважды.
Недавно я узнал, что Postgres поддерживает «оконные функции», которые в основном похожи на GROUP BY для определенного столбца. Я изменил свой запрос, чтобы использовать их так:
SELECT
perm,
combo,
avg( value ) as perm_average_value,
avg( avg( value ) ) over w_combo AS combo_average_value,
stddev_pop( value ) as perm_stddev,
stddev_pop( avg( value ) ) over w_combo as combo_stddev,
count( * ) as perm_count,
sum( count( * ) ) over w_combo AS combo_count
FROM foo
GROUP BY perm, combo
WINDOW w_combo AS ( PARTITION BY combo );
Хотя это работает для столбца «combo_count», столбцы «combo_average_value» и «combo_stddev» более не точны. Похоже, что среднее значение берется для каждой перестановки, а затем усредняется во второй раз для каждой комбинации, что неверно.
Как я могу это исправить? Можно ли здесь использовать оконные функции в качестве оптимизации?
источник
Ответы:
Вы можете иметь оконные функции для результата агрегатных функций на одном уровне запросов.
Все это будет хорошо работать после нескольких модификаций - за исключением того, что оно не будет работать для стандартного отклонения по математическому принципу . Соответствующие расчеты не являются линейными, поэтому вы не можете просто объединить стандартные отклонения подгрупп населения.
Для
combo_average_value
вас потребуется это выражениеТак как вам нужно средневзвешенное значение. (Средняя группа из 10 человек весит больше, чем группа из 2 человек!)
Это работает :
Я использую здесь два разных окна и сокращаю количество строк, к
DISTINCT
которым применяется даже после оконных функций.Но я серьезно сомневаюсь, что это будет быстрее, чем ваш оригинальный запрос. Я уверен, что это не так.
Лучшая производительность с измененной разметкой стола
Размер массива составляет 24 байта (небольшие различия в зависимости от типа). Кроме того, у вас, кажется, довольно много элементов в массиве и много повторений. Для такой огромной таблицы, как ваша, было бы полезно нормализовать схему. Пример макета:
Если вам не нужна ссылочная целостность, вы можете опустить ограничения внешнего ключа.
Соединение с
combo_id
также может быть помещено в таблицуperm
, но в этом сценарии я бы сохранил его (слегка ненормализованный)value
для повышения производительности.Это приведет к размеру строки 32 байта (заголовок кортежа + заполнение: 24 байта, 2 x int (8 байт), без заполнения) плюс неизвестный размер вашего
numeric
столбца. (Если вам не нужна предельная точность, столбецdouble precision
илиreal
столбец тоже могут это сделать.)Подробнее о физическом хранении в этом связанном ответе на SO или здесь:
Настройка PostgreSQL для производительности чтения
В любом случае, это лишь малая часть того, что у вас есть сейчас, и ваш запрос будет намного быстрее только по размеру. Группировка и сортировка по простым целым числам также намного быстрее.
Вы бы первый агрегат в подзапрос и затем присоединиться к
perm
иcombo
для лучшей производительности.источник
foo
таблицы столбцы , которые не были релевантными. В действительности, есть еще несколько столбцов, которые не используются в этом запросе, поэтому я не уверен, что нормализация перестановок и комбинаций обеспечит значительное повышение скорости для этого конкретного случая использования.