#postgresql #aggregates
#postgresql #агрегирует
Вопрос:
У меня есть некоторые данные, которые выглядят примерно так после того, как я запустил над ними несколько фильтров:
Business Type | Business City | Min Rating | Max Rating
-------------- --------------- ------------ -----------
Restaurant | Barcelona | 2 | 8
Restaurant | Barcelona | 1 | 10
Restaurant | Madrid | 4 | 8
Diner | Madrid | 5 | 8
Diner | Madrid | 1 | 8
Diner | Barcelona | 3 | 8
Мне нужно вернуть только одну строку для каждого типа бизнеса и комбинации городов. Возвращаемая мной строка должна быть той, в которой минимальные и максимальные значения наиболее близки к определенным числам. Например, я хочу вернуть строку, ближайшую к минимальному рейтингу 3 и максимальному рейтингу 7. Это привело бы к:
Business Type | Business City | Min Rating | Max Rating
-------------- --------------- ------------ -----------
Restaurant | Barcelona | 2 | 8
Restaurant | Madrid | 4 | 8
Diner | Madrid | 5 | 8
Diner | Barcelona | 3 | 8
Это в приложении Rails 5, использующем ActiveRecord. Я открыт для использования Arel, ActiveRecord DSL или PostgreSQL.
Комментарии:
1. Упорядочите их по разнице в конкретных значениях и примените ограничение.
2. Похоже, все, что вам нужно, это
GROUP BY
для столбцов, которые должны быть уникальными, иmin
илиmax
для агрегированных столбцов. Что меня смущает, так это то, что вы суммируете «Минимальный рейтинг», беря максимальный…3. @Marek Мне нужно по одному для каждой сгруппированной пары. Ваше предложение ограничило бы количество строк в целом.
4. @Laurenz Я не ищу максимальное или минимальное значение. Мне нужен тот, в котором оба столбца находятся ближе всего к своим целевым номерам, которые не совпадают. Целевое число также всегда будет находиться в середине всех чисел, вот почему я привел пример таким образом. В моем примере я ищу 3 в столбце min и 7 в столбце max. Это не реальные данные, с которыми я работаю, но это простой пример, который показывает, что мне нужно сделать.
Ответ №1:
Для этого вы можете определить пользовательскую агрегатную функцию:
CREATE FUNCTION closer_to(integer, integer, integer) RETURNS integer
LANGUAGE sql AS
'SELECT CASE WHEN abs($1 - $3) < abs($2 - $3) THEN $1 ELSE $2 END';
CREATE AGGREGATE closest_to(integer, integer) (
STYPE = integer,
SFUNC = closer_to
);
Затем вы можете записать свой запрос в виде:
SELECT "Business Type", "Business City",
closest_to("Min Rating", 3) AS "Min Rating",
closest_to("Max Rating", 7) AS "Max Rating",
FROM mytable
GROUP BY "Business Type", "Business City";