如何选择一行中加入的最低和最高值postgres

时间:2015-09-30 08:49:49

标签: sql postgresql

我的数据格式与此类似

+--------+------------+-------+
| 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,但这不是我需要的。

4 个答案:

答案 0 :(得分: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)

替代那些不能使用具有强大功能的Postgres的人:

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;

答案 1 :(得分:2)

您的结果表示例的三个行的最小值和最大值都是错误的;)

另一种方法 - 与上面的答案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

答案 2 :(得分:1)

这种性质的问题(获得表中每个组的最大,最小,平均,第一,最后......)最好用所谓的window function来解决。使用窗口函数,您可以将数据PARTITION分组(在"type"列上),然后在分区上应用一些window functionaggregate 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一样)或连接。在较大的表上,这应该会在性能上产生很大的差异。此外,它还可以根据其他要求进行更好的扩展(例如将平均价格添加到输出中)。

答案 3 :(得分:0)

use ErrorResponseWrapper::ErrorResponse;