2016-10-30 2 views
2

У меня возникла простая проблема с SQL-запросом, который я не знаю, как решать.SQL min/max со всеми полями

У меня есть таблица со следующей структурой

CITY COUNTRY DATES TEMPERATURE 

Обратите внимание, что для данной страны, я могу иметь несколько городов. И для данного города у меня есть несколько рядов, дающих мне ТЕМПЕРАТУРУ на каждую доступную ДАТА. Это всего лишь временная серия.

Я хотел бы написать запрос, который дает мне для каждого города DATE, где ТЕМПЕРАТУРА - это MIN и DATE, где ТЕМПЕРАТУРА - МАКС. Запрос должен вернуть что-то вроде этого:

CITY COUNTRY DATE_MIN_TEMPERATURE MIN_TEMPERATURE DATE_MAX_TEMPERATURE MAX_TEMPERATURE 

Любая идея о том, как этого достичь?

С наилучшими пожеланиями,

Deny

+0

Что вы хотите отображается, если для данного города есть два разных дня, когда была достигнута минимальная температура? – mathguy

+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; 

Обратите внимание, что это возвращает только одну дату, если есть связи. Если вы хотите справиться с этим, потребуется больше логики:

+0

Слишком плохо, что функции 'first' и' last' не работают с 'listagg' как агрегированная функция! (На самом деле непонятно, почему они этого не делают, но они этого не делают.) Было бы здорово объединить обе идеи в одном. – mathguy

+0

err ... второе решение имеет несколько недостающих ключевых слов, опечаток, «дата» не работает как имя столбца и т. Д. После устранения всех этих проблем решение дает неправильный ответ. (Вероятно, потому что второй вызов row_number() должен упорядочиваться по температуре ** desc **?) И это не должно быть 'row_number()', это должно быть 'rank()' в любом случае. Путь слишком много ошибок. – mathguy

+0

@mathguy. , , 'date' - это столбец, предоставленный OP. Я не вижу причин скрывать это, потому что это, вероятно, не настоящее имя столбца. Я думаю, что другие опечатки фиксированы. Комментарий к 'rank()' очень важен, но на самом деле это первый запрос, который фактически отвечает на вопрос OP. –

0

В Oracle 11 и выше вы можете использовать PIVOT. В приведенном ниже решении я использую LISTAGG для отображения всех дат в случае связей. Другим вариантом является, в случае связей, показать самую последнюю дату, когда была достигнута экстремальная температура; если это является предпочтительным, просто замените LISTAGG(dt, ....) (включая предложение WITHIN GROUP) MAX(dt). Однако в этом случае первое решение, предлагаемое Гордоном (с использованием функции first), в любом случае более эффективно - нет необходимости в повороте.

Обратите внимание, что я изменил «дату» на «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. 
0

Вместо keep/dense_rank first функции вы можете также использовать FIRST_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; 
Смежные вопросы