SQL min / max包含所有字段

时间:2016-10-30 13:11:16

标签: sql oracle greatest-n-per-group

我面临一个SQL查询的简单问题,我不知道如何处理。

我有一个包含以下结构的表

CITY COUNTRY DATES TEMPERATURE

请注意,对于特定国家/地区,我可以拥有多个城市。并且,对于给定的城市,我有几行在每个可用的日期给我温度。这只是一个时间系列。

我想写一个查询,它为每个城市提供了TEMPERATURE为MIN的DATE和TEMPERATURE为MAX的DATE。查询应返回类似的内容:

CITY COUNTRY DATE_MIN_TEMPERATURE MIN_TEMPERATURE DATE_MAX_TEMPERATURE MAX_TEMPERATURE

关于如何实现这一目标的任何想法?

致以最诚挚的问候,

拒绝

3 个答案:

答案 0 :(得分:1)

Oracle为此提供了keep / dense_rank first

select city,
       min(temperature) as min_temperature,
       max(date) keep (dense_rank first order by temperature asc) as min_temperature_date,
       max(temperature) as max_temperature,
       max(date) keep (dense_rank first order by temperature desc) as max_temperature_date
from t
group by city;

请注意,如果存在关联,则只返回一个日期。如果你想处理它,需要更多的逻辑:

select city, min(temperature) as min_temperature,
       listagg(case when seqnum_min = 1 then date end, ',') within group (order by date) as mindates,
       max(temperature) as max_temperature,
       listagg(case when seqnum_max = 1 then date end, ',') within group (order by date) as maxdates,
from (select t.*,
             rank() over (partition by city order by temperature) as seqnum_min,
             rank() over (partition by city order by temperature desc) as seqnum_max
      from t
     ) t
where seqnum_min = 1 or seqnum_max = 1
group by city;

答案 1 :(得分:0)

在Oracle 11及更高版本中,您可以使用PIVOT。在下面的解决方案中,我使用LISTAGG显示关系时的所有日期。另一种选择是,在关系的情况下,显示达到极端温度的最近日期;如果这是首选,只需将LISTAGG(dt, ....)(包括WITHIN GROUP子句)替换为MAX(dt)。但是,在这种情况下,Gordon提供的第一个解决方案(使用first函数)无论如何都更有效 - 无需转动。

请注意,我更改了" date"到" dt" - DATE是Oracle中的保留字。我还首先按国家/地区显示行,然后是城市(更符合逻辑的顺序)。我在WITH子句中创建了测试数据,但解决方案是注释行下面的所有内容。

with
     inputs ( city, country, dt, temperature ) as (
       select 'Palermo', 'Italy'  , date '2014-02-13',  3 from dual union all
       select 'Palermo', 'Italy'  , date '2002-01-23',  3 from dual union all
       select 'Palermo', 'Italy'  , date '1998-07-22', 42 from dual union all
       select 'Palermo', 'Italy'  , date '1993-08-24', 30 from dual union all
       select 'Maseru' , 'Lesotho', date '1994-01-11', 34 from dual union all
       select 'Maseru' , 'Lesotho', date '2004-08-13', 12 from dual
     )
--  >>  end test data; solution (SQL query) begins with the next line
select country, city,
       "'min'_DT" as date_min_temp, "'min'_TEMP" as min_temp,
       "'max'_DT" as date_max_temp, "'max'_TEMP" as max_temp
from ( 
       select city, country, dt, temperature,
              case when temperature = min(temperature) 
                        over (partition by city, country) then 'min'
                   when temperature = max(temperature) 
                        over (partition by city, country) then 'max' 
                   end as flag
       from inputs
     )
pivot ( listagg(to_char(dt, 'dd-MON-yyyy'), ', ') 
        within group (order by dt) as dt, min(temperature) as temp
        for flag in ('min', 'max'))
order by country, city    --  ORDER BY is optional
;

COUNTRY CITY    DATE_MIN_TEMP              MIN_TEMP DATE_MAX_TEMP    MAX_TEMP
------- ------- ------------------------ ---------- -------------- ----------
Italy   Palermo 23-JAN-2002, 13-FEB-2014          3 22-JUL-1998            42
Lesotho Maseru  13-AUG-2004                      12 11-JAN-1994            34

2 rows selected.

答案 2 :(得分:0)

您可以使用keep/dense_rank firstFIRST_VALUE代替LAST_VALUE功能:

select distinct city,
   MIN(temperature) OVER (PARTITION BY city) as min_temperature,
   FIRST_VALUE(date) OVER (PARTITION BY city ORDER BY temperature) AS min_temperature_date,
   MAX(temperature) OVER (PARTITION BY city) as max_temperature,
   LAST_VALUE(date) OVER (PARTITION BY city ORDER BY temperature) AS max_temperature_date
FROM t;