2015-09-30 2 views
3

У меня есть данные в том же формате, как этоКак выбрать самый низкий и самое высокое значение объединены в одну строку Postgres

+--------+------------+-------+ 
| type | variety | price | 
+--------+------------+-------+ 
| apple | gala  | 2.79 | 
| apple | fuji  | 0.24 | 
| apple | limbertwig | 2.87 | 
| orange | valencia | 3.59 | 
| orange | navel  | 9.36 | 
| pear | bradford | 6.05 | 
| pear | bartlett | 2.14 | 
| cherry | bing  | 2.55 | 
| cherry | chelan  | 6.33 | 
+--------+------------+-------+ 

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

+--------+------------+-------+-------+ 
| type | variety | min | max | 
+--------+------------+-------+-------+ 
| apple | limbertwig | 0.24 | 2.87 | 
| orange | navel  | 9.36 | 3.59 | 
| pear | bradford | 6.05 | 2.14 | 
| cherry | chelan  | 6.33 | 2.55 | 
+--------+------------+-------+-------+ 

Что является лучшим способом для достижения этой цели с помощью Postgres?

Я нашел этот сайт: How to select the first/least/max row per group in SQL, но это не совсем то, что мне нужно.

ответ

2

Ищите фрукты с самой низкой ценой:

select distinct on (type) type, variety, price 
from fruits 
order by 1, 3; 

type | variety | price 
--------+----------+------- 
apple | fuji  | 0.24 
cherry | bing  | 2.55 
orange | valencia | 3.59 
pear | bartlett | 2.14 
(4 rows) 

Найти фрукты с самой высокой ценой:

select distinct on (type) type, variety, price 
from fruits 
order by 1, 3 desc; 

type | variety | price 
--------+------------+------- 
apple | limbertwig | 2.87 
cherry | chelan  | 6.33 
orange | navel  | 9.36 
pear | bradford | 6.05 
(4 rows) 

Объединить два запроса:

select 
    f1.type, 
    f1.variety min_variety, f1.price min_price, 
    f2.variety max_variety, f2.price max_price 
from (
    select distinct on (type) type, variety, price 
    from fruits 
    order by 1, 3) f1 
join (
    select distinct on (type) type, variety, price 
    from fruits 
    order by 1, 3 desc) f2 
on f1.type = f2.type 

type | min_variety | min_price | max_variety | max_price 
--------+-------------+-----------+-------------+----------- 
apple | fuji  |  0.24 | limbertwig |  2.87 
cherry | bing  |  2.55 | chelan  |  6.33 
orange | valencia |  3.59 | navel  |  9.36 
pear | bartlett |  2.14 | bradford |  6.05 
(4 rows) 

Альтернативу для тех, кто не может использовать Postgr эс со своими большими особенностями:

select 
    f1.type, 
    f1.variety min_variety, f1.price min_price, 
    f2.variety max_variety, f2.price max_price 
from (
    select f.type, f.variety, f.price 
    from (
     select type, min(price) minprice 
     from fruits group by type 
     ) x 
    join fruits f on f.type = x.type and f.price = x.minprice 
    ) f1 
join (
    select f.type, f.variety, f.price 
    from (
     select type, max(price) maxprice 
     from fruits group by type 
     ) x 
    join fruits f on f.type = x.type and f.price = x.maxprice 
    ) f2 
on f1.type = f2.type 
order by 1; 
+0

Сделал sqlfiddle этого решения и увидел, что он делает так, как ожидалось http://sqlfiddle.com/#!15/19b44/1 НО, как я могу реализовать это в zend, поэтому, если позже мы перейдем от postgres к чему-то еще, как оракул, и он все еще работает (DISTINCT ON - postgres only afaik) – Hammersholt

+0

См. Отредактированный ответ. Я не использую sqlfiddle, потому что он перегружен в последнее время. – klin

+0

Мне удалось выжать из sqlfiddle ответ [для MySQL] (http://sqlfiddle.com/#!9/19b44/1). – klin

1

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

SELECT DISTINCT "type", last_value(variety) OVER w AS variety 
     first_value(price) OVER w AS min, last_value(price) OVER w AS max 
FROM my_table 
WINDOW w AS (PARTITION BY "type" ORDER BY price 
      RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) 
ORDER BY 1; 

Обратите внимание, что функции окна или агрегатные функции добавляют столбцы к выходу, как в обычном случае. В ответе выше пункт DISTINCT используется для получения только одной строки для каждого из «типов» фруктов. Без этого предложения вы получите строку вывода для каждой строки ввода, с повторными данными для каждого «типа» фруктов.

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

+0

никогда не знал об окне howwever это не похоже на работу http://sqlfiddle.com/#!15/7a6e5/2 – Hammersholt

+0

вы можете добавить некоторые объяснения, а не только код? – eirikir

+0

@eirikir и @Hammersholt В ответ нужно предложение «RANGE»; ответ исправлен и добавлено объяснение. – Patrick

1

Ваши результаты таблицы образец имеет минимальное и максимальное по неправильному пути для трех рядов;)

Другой способ сделать это - подобный ответ 1 выше, с , CTE:

WITH mini AS (
    select type 
    , MIN(price) as minprice 
    FROM fruits 
    GROUP BY type 
), maxi AS (
    select type 
    , MAX(price) as maxprice 
    FROM fruits 
    GROUP BY type 
) 
SELECT fruits.type 
, fruits.variety as max_variety 
, mini.minprice 
, maxi.maxprice 
FROM fruits 
JOIN mini ON mini.type = fruits.type 
JOIN maxi ON maxi.type = fruits.type AND maxi.maxprice = fruits.price 
WHERE fruits.price = maxi.maxprice 
AND fruits.type = maxi.type 
ORDER BY fruits.type