选择所有记录的列最大值和最小值

时间:2018-05-02 10:47:55

标签: sql oracle

我有一张桌子如下;我想让列名具有最大值和最小值,除了所有记录的种群列(当然它将具有最大值)。

State   Population   age_below_18  age_18_to_50   age_50_above
 1         1000          250          600            150
 2         4200          400          300            3500

结果:

   State    Population   Maximum_group    Minimum_group    Max_value   Min_value
     1         1000      age_18_to_50      age_50_above      600         150
     2         4200      age_50_above      age_18_to_50     3500         300

3 个答案:

答案 0 :(得分:4)

假设没有值为NULL,您可以使用greatest()least()

select state, population,
       (case when age_below_18 = greatest(age_below_18, age_18_to_50, age_50_above)
             then 'age_below_18'
             when age_below_18 = greatest(age_below_18, age_18_to_50, age_50_above)
             then 'age_18_to_50'
             when age_below_18 = greatest(age_below_18, age_18_to_50, age_50_above)
             then 'age_50_above'
         end) as maximum_group,
       (case when age_below_18 = least(age_below_18, age_18_to_50, age_50_above)
             then 'age_below_18'
             when age_below_18 = least(age_below_18, age_18_to_50, age_50_above)
             then 'age_18_to_50'
             when age_below_18 = least(age_below_18, age_18_to_50, age_50_above)
             then 'age_50_above'
         end) as minimum_group,
        greatest(age_below_18, age_18_to_50, age_50_above) as maximum_value,
        least(age_below_18, age_18_to_50, age_50_above) as minimum_value
from t;

如果您的结果集实际上是由查询生成的,则可能有更好的方法。

另一种方法" unpivots"数据,然后重新聚合:

select state, population,
       max(which) over (dense_rank first_value order by val desc) as maximum_group,
       max(which) over (dense_rank first_value order by val asc) as minimum_group,
       max(val) as maximum_value,
       min(val) as minimum_value
from ((select state, population, 'age_below_18' as which, age_below_18 as val
       from t
      ) union all
      (select state, population, 'age_18_to_50' as which, age_18_to_50 as val
       from t
      ) union all
      (select state, population, 'age_50_above' as which, age_50_above as val
       from t
      )
     ) t
group by state, population;

这种方法的性能要比第一种方法低,但随着值的增加,它可能更容易实现。但是,Oracle 12C支持横向连接,类似的方法可以提供有竞争力的性能。

答案 1 :(得分:2)

with CTE as (
    select T.* 
        --step2: rank value
        ,RANK() OVER (PARTITION BY "State", "Population" order by "value") "rk" 
    from (
        --step1: union merge three column to on column
        select 
         "State", "Population", 
         'age_below_18' as GroupName,
         "age_below_18" as "value"
        from TestTable
        union all
        select 
         "State", "Population",     
         'age_18_to_50' as GroupName,
         "age_18_to_50" as "value"
        from TestTable
        union all
        select 
         "State", "Population",     
         'age_50_above' as GroupName,
         "age_50_above" as "value"
        from TestTable   
    ) T
)
select T1."State", T1."Population"
    ,T3.GroupName Maximum_group
    ,T4.GroupName Minimum_group    
    ,T3."value" Max_value   
    ,T4."value" Min_value

--step3: max rank get maxvalue,min rank get minvalue   
from (select "State", "Population",max( "rk") as Max_rank from CTE group by "State", "Population") T1
left join (select "State", "Population",min( "rk") as Min_rank from CTE group by "State", "Population") T2 
    on T1."State" = T2."State" and T1."Population" = T2."Population"
left join CTE T3 on T3."State" = T1."State" and T3."Population" = T1."Population" and T1.Max_rank = T3."rk"
left join CTE T4 on T4."State" = T2."State" and T4."Population" = T2."Population" and T2.Min_rank = T4."rk"

SQL Fiddle DEMO LINK

希望它对你有所帮助:)。

答案 2 :(得分:1)

另一种选择:使用UNPIVOT()的组合,“将列旋转成行”(参见:documentation)和分析函数,“根据一组行计算聚合值”(文档{ {3}})例如

测试数据

select * from T ;

STATE  POPULATION  YOUNGERTHAN18  BETWEEN18AND50  OVER50  
1      1000        250            600             150     
2      4200        400            300             3500 

<强> UNPIVOT

select *
from T
unpivot  (  
  quantity for agegroup in (
    youngerthan18  as 'youngest'
  , between18and50 as 'middleaged'
  , over50         as 'oldest'
  )
);

-- result
STATE  POPULATION  AGEGROUP    QUANTITY  
1      1000        youngest    250       
1      1000        middleaged  600       
1      1000        oldest      150       
2      4200        youngest    400       
2      4200        middleaged  300       
2      4200        oldest      3500 

包括分析函数

select distinct
  state
, population
, max( quantity ) over ( partition by state ) maxq
, min( quantity ) over ( partition by state ) minq
, first_value ( agegroup ) over ( partition by state order by quantity desc ) biggest_group
, first_value ( agegroup ) over ( partition by state order by quantity ) smallest_group
from T
  unpivot  (  
    quantity for agegroup in (
      youngerthan18  as 'youngest'
    , between18and50 as 'middleaged'
    , over50         as 'oldest'
    )
  )
;

-- result
STATE  POPULATION  MAXQ  MINQ  BIGGEST_GROUP  SMALLEST_GROUP  
1      1000        600   150   middleaged     oldest          
2      4200        3500  300   oldest         middleaged

使用Oracle 11g(请参阅here)和Oracle 12c进行测试的示例。

注意:{1}列(标题)需要调整(根据您的要求)。 {2}如果原始表中有NULL,则应调整查询,例如使用NVL()。

所描述的方法的一个优点是:即使使用更多“类别”,代码仍将保持相当清晰。例如,当与11个年龄组一起工作时,查询可能看起来像......

select distinct
  state
, population
, max( quantity ) over ( partition by state ) maxq
, min( quantity ) over ( partition by state ) minq
, first_value ( agegroup ) over ( partition by state order by quantity desc ) biggest_group
, first_value ( agegroup ) over ( partition by state order by quantity ) smallest_group
from T
  unpivot  (  
    quantity for agegroup in (
      y10     as 'youngerthan10'   
    , b10_20  as 'between10and20'
    , b20_30  as 'between20and30'
    , b30_40  as 'between30and40'
    , b40_50  as 'between40and50'
    , b50_60  as 'between50and60'
    , b60_70  as 'between60and70'
    , b70_80  as 'between70and80'
    , b80_90  as 'between80and90'
    , b90_100 as 'between90and100'
    , o100    as 'over100'
    )
  )
order by state
;

请参阅dbfiddle